File: Metrics\CollectedMeasurement.cs
Web Access
Project: src\src\Libraries\Microsoft.Extensions.Diagnostics.Testing\Microsoft.Extensions.Diagnostics.Testing.csproj (Microsoft.Extensions.Diagnostics.Testing)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Microsoft.Shared.Diagnostics;
 
namespace Microsoft.Extensions.Diagnostics.Metrics.Testing;
 
/// <summary>
/// Represents a single measurement performed by an instrument.
/// </summary>
/// <typeparam name="T">The type of metric measurement value.</typeparam>
[DebuggerDisplay("{DebuggerToString(),nq}")]
public sealed class CollectedMeasurement<T>
    where T : struct
{
    /// <summary>
    /// Initializes a new instance of the <see cref="CollectedMeasurement{T}"/> class.
    /// </summary>
    /// <param name="value">The measurement's value.</param>
    /// <param name="tags">The dimensions of this measurement.</param>
    /// <param name="timestamp">The time that the measurement occurred at.</param>
    internal CollectedMeasurement(T value, ReadOnlySpan<KeyValuePair<string, object?>> tags, DateTimeOffset timestamp)
    {
        var d = new Dictionary<string, object?>();
        foreach (var tag in tags)
        {
            d[tag.Key] = tag.Value;
        }
 
        Tags = d;
        Timestamp = timestamp;
        Value = value;
    }
 
    /// <summary>
    /// Gets a measurement's value.
    /// </summary>
    public T Value { get; }
 
    /// <summary>
    /// Gets a timestamp indicating when the measurement was recorded.
    /// </summary>
    public DateTimeOffset Timestamp { get; }
 
    /// <summary>
    /// Gets the measurement's dimensions.
    /// </summary>
    public IReadOnlyDictionary<string, object?> Tags { get; }
 
    /// <summary>
    /// Checks that the measurement includes a specific set of tags with specific values.
    /// </summary>
    /// <param name="tags">The set of tags to check.</param>
    /// <returns><see langword="true"/> if all the tags exist in the measurement with matching values, otherwise <see langword="false"/>.</returns>
    public bool ContainsTags(params KeyValuePair<string, object?>[] tags)
    {
        foreach (var kvp in Throw.IfNull(tags))
        {
            if (!Tags.TryGetValue(kvp.Key, out var value))
            {
                return false;
            }
 
            if (!object.Equals(kvp.Value, value))
            {
                return false;
            }
        }
 
        return true;
    }
 
    /// <summary>
    /// Checks that the measurement includes a specific set of tags with any value.
    /// </summary>
    /// <param name="tags">The set of tag names to check.</param>
    /// <returns><see langword="true"/> if all the tags exist in the measurement, otherwise <see langword="false"/>.</returns>
    public bool ContainsTags(params string[] tags)
    {
        foreach (var key in Throw.IfNull(tags))
        {
            if (!Tags.ContainsKey(key))
            {
                return false;
            }
        }
 
        return true;
    }
 
    /// <summary>
    /// Checks that the measurement has an exactly matching set of tags with specific values.
    /// </summary>
    /// <param name="tags">The set of tags to check.</param>
    /// <returns><see langword="true"/> if all the tags exist in the measurement with matching values, otherwise <see langword="false"/>.</returns>
    public bool MatchesTags(params KeyValuePair<string, object?>[] tags) => ContainsTags(tags) && (Tags.Count == tags.Length);
 
    /// <summary>
    /// Checks that the measurement has a exactly matching set of tags with any value.
    /// </summary>
    /// <param name="tags">The set of tag names to check.</param>
    /// <returns><see langword="true"/> if all the tag names exist in the measurement, otherwise <see langword="false"/>.</returns>
    public bool MatchesTags(params string[] tags) => ContainsTags(Throw.IfNull(tags)) && (Tags.Count == tags.Length);
 
    internal string DebuggerToString() => $"{Value} @ {Timestamp.ToString("HH:mm:ss.ffff", CultureInfo.InvariantCulture)}";
}