Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update metrics to support OpenTelemetry specification #5120

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,37 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
public class CounterMetadata
{
public CounterMetadata(string providerName, string counterName, string meterTags, string instrumentTags, string scopeHash)
: this(
providerName,
providerVersion: null,
counterName,
counterUnit: null,
counterDescription: null,
meterTags,
instrumentTags,
scopeHash)
{
}

public CounterMetadata(string providerName, string providerVersion, string counterName, string counterUnit, string counterDescription, string meterTags, string instrumentTags, string scopeHash)
{
ProviderName = providerName;
ProviderVersion = providerVersion;
CounterName = counterName;
CounterUnit = counterUnit;
CounterDescription = counterDescription;
MeterTags = meterTags;
InstrumentTags = instrumentTags;
ScopeHash = scopeHash;
}

public CounterMetadata() { }

public string ProviderName { get; private set; }
public string ProviderVersion { get; private set; }
public string CounterName { get; private set; }
public string CounterUnit { get; private set; }
public string CounterDescription { get; private set; }
public string MeterTags { get; private set; }
public string InstrumentTags { get; private set; }
public string ScopeHash { get; private set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ protected CounterPayload(DateTime timestamp,

public string Unit { get; }

public double Value { get; }
public double Value { get; protected set; }

public DateTime Timestamp { get; }
public DateTime Timestamp { get; protected set; }

public float Interval { get; }

Expand Down Expand Up @@ -86,7 +86,38 @@ protected MeterPayload(DateTime timestamp,
{
}

public override bool IsMeter => true;
public sealed override bool IsMeter => true;
}

internal abstract class MeterInstrumentDeltaMeasurementPayload : MeterPayload
{
protected MeterInstrumentDeltaMeasurementPayload(
DateTime timestamp,
CounterMetadata counterMetadata,
string displayName,
string unit,
double value,
CounterType counterType,
string valueTags,
EventType eventType)
: base(timestamp, counterMetadata, displayName, unit, value, counterType, valueTags, eventType)
{
}

public abstract bool SupportsDelta { get; }

public virtual void Combine(MeterInstrumentDeltaMeasurementPayload other)
{
if (other.Timestamp > Timestamp)
{
Timestamp = other.Timestamp;
}
}
}

internal interface IRatePayload
{
double Rate { get; }
}

internal sealed class GaugePayload : MeterPayload
Expand All @@ -100,14 +131,29 @@ public GaugePayload(CounterMetadata counterMetadata, string displayName, string
}
}

internal class UpDownCounterPayload : MeterPayload
internal class UpDownCounterPayload : MeterInstrumentDeltaMeasurementPayload, IRatePayload
{
public UpDownCounterPayload(CounterMetadata counterMetadata, string displayName, string displayUnits, string valueTags, double value, DateTime timestamp) :
public UpDownCounterPayload(CounterMetadata counterMetadata, string displayName, string displayUnits, string valueTags, double rate, double value, DateTime timestamp) :
base(timestamp, counterMetadata, displayName, displayUnits, value, CounterType.Metric, valueTags, EventType.UpDownCounter)
{
// In case these properties are not provided, set them to appropriate values.
string counterName = string.IsNullOrEmpty(displayName) ? counterMetadata.CounterName : displayName;
DisplayName = !string.IsNullOrEmpty(displayUnits) ? $"{counterName} ({displayUnits})" : counterName;
Rate = rate;
}

public double Rate { get; private set; }

public override bool SupportsDelta => false;

public override void Combine(MeterInstrumentDeltaMeasurementPayload other)
{
if (other is UpDownCounterPayload upDownCounterPayload)
{
Rate += upDownCounterPayload.Rate;
}

base.Combine(other);
}
}

Expand All @@ -131,9 +177,8 @@ public CounterEndedPayload(CounterMetadata counterMetadata, DateTime timestamp)
/// This gets generated for Counter instruments from Meters. This is used for pre-.NET 8 versions of MetricsEventSource that only reported rate and not absolute value,
/// or for any tools that haven't opted into using RateAndValuePayload in the CounterConfiguration settings.
/// </summary>
internal sealed class RatePayload : MeterPayload
internal sealed class RatePayload : MeterInstrumentDeltaMeasurementPayload, IRatePayload
{

public RatePayload(CounterMetadata counterMetadata, string displayName, string displayUnits, string valueTags, double rate, double intervalSecs, DateTime timestamp) :
base(timestamp, counterMetadata, displayName, displayUnits, rate, CounterType.Rate, valueTags, EventType.Rate)
{
Expand All @@ -143,13 +188,24 @@ public RatePayload(CounterMetadata counterMetadata, string displayName, string d
string intervalName = intervalSecs.ToString() + " sec";
DisplayName = $"{counterName} ({unitsName} / {intervalName})";
}

public double Rate => Value;

public override bool SupportsDelta => true;

public override void Combine(MeterInstrumentDeltaMeasurementPayload other)
{
Value += other.Value;

base.Combine(other);
}
}

/// <summary>
/// Starting in .NET 8, MetricsEventSource reports counters with both absolute value and rate. If enabled in the CounterConfiguration and the new value field is present
/// then this payload will be created rather than the older RatePayload. Unlike RatePayload, this one treats the absolute value as the primary statistic.
/// </summary>
internal sealed class CounterRateAndValuePayload : MeterPayload
internal sealed class CounterRateAndValuePayload : MeterInstrumentDeltaMeasurementPayload, IRatePayload
{
public CounterRateAndValuePayload(CounterMetadata counterMetadata, string displayName, string displayUnits, string valueTags, double rate, double value, DateTime timestamp) :
base(timestamp, counterMetadata, displayName, displayUnits, value, CounterType.Metric, valueTags, EventType.Rate)
Expand All @@ -162,6 +218,18 @@ public CounterRateAndValuePayload(CounterMetadata counterMetadata, string displa
}

public double Rate { get; private set; }

public override bool SupportsDelta => true;

public override void Combine(MeterInstrumentDeltaMeasurementPayload other)
{
if (other is CounterRateAndValuePayload counterRateAndValuePayload)
{
Rate += counterRateAndValuePayload.Rate;
}

base.Combine(other);
}
}

internal record struct Quantile(double Percentage, double Value);
Expand All @@ -182,17 +250,36 @@ public PercentilePayload(CounterMetadata counterMetadata, string displayName, st
// dotnet-counters created a separate payload for each quantile (multiple payloads per TraceEvent).
// AggregatePercentilePayload allows dotnet-monitor to construct a PercentilePayload for individual quantiles
// like dotnet-counters, while still keeping the quantiles together as a unit.
internal sealed class AggregatePercentilePayload : MeterPayload
internal sealed class AggregatePercentilePayload : MeterInstrumentDeltaMeasurementPayload
{
public AggregatePercentilePayload(CounterMetadata counterMetadata, string displayName, string displayUnits, string valueTags, IEnumerable<Quantile> quantiles, DateTime timestamp) :
public AggregatePercentilePayload(CounterMetadata counterMetadata, string displayName, string displayUnits, string valueTags, int count, double sum, IEnumerable<Quantile> quantiles, DateTime timestamp) :
base(timestamp, counterMetadata, displayName, displayUnits, 0.0, CounterType.Metric, valueTags, EventType.Histogram)
{
Count = count;
Sum = sum;
//string counterName = string.IsNullOrEmpty(displayName) ? name : displayName;
//DisplayName = !string.IsNullOrEmpty(displayUnits) ? $"{counterName} ({displayUnits})" : counterName;
Quantiles = quantiles.ToArray();
}

public Quantile[] Quantiles { get; }
public int Count { get; private set; }

public double Sum { get; private set; }

public Quantile[] Quantiles { get; private set; }

public override bool SupportsDelta => true;

public override void Combine(MeterInstrumentDeltaMeasurementPayload other)
{
if (other is AggregatePercentilePayload aggregatePercentilePayload)
{
Count += aggregatePercentilePayload.Count;
Sum += aggregatePercentilePayload.Sum;
}

base.Combine(other);
}
}

internal sealed class ErrorPayload : MeterPayload
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,16 @@ internal static class TraceEventExtensions
private static Dictionary<int, CounterMetadata> counterMetadataById = new();
private static HashSet<string> inactiveSharedSessions = new(StringComparer.OrdinalIgnoreCase);

private static CounterMetadata AddCounterMetadata(string providerName, string counterName, int? id, string meterTags, string instrumentTags, string scopeHash)
private static CounterMetadata AddCounterMetadata(
string providerName,
string counterName,
int? id,
string meterTags,
string instrumentTags,
string scopeHash,
string providerVersion = null,
string counterUnit = null,
string counterDescription = null)
{
CounterMetadata metadata;
if (id.HasValue && counterMetadataById.TryGetValue(id.Value, out metadata))
Expand All @@ -67,7 +76,7 @@ private static CounterMetadata AddCounterMetadata(string providerName, string co
}

// no pre-existing counter metadata was found, create a new one
metadata = new CounterMetadata(providerName, counterName, meterTags, instrumentTags, scopeHash);
metadata = new CounterMetadata(providerName, providerVersion, counterName, counterUnit, counterDescription, meterTags, instrumentTags, scopeHash);
if (id.HasValue)
{
counterMetadataById.TryAdd(id.Value, metadata);
Expand Down Expand Up @@ -266,21 +275,26 @@ private static void HandleBeginInstrumentReporting(TraceEvent traceEvent, Counte
}

string meterName = (string)traceEvent.PayloadValue(1);
//string meterVersion = (string)obj.PayloadValue(2);
string meterVersion = (string)traceEvent.PayloadValue(2);
string instrumentName = (string)traceEvent.PayloadValue(3);

if (!counterConfiguration.CounterFilter.IsIncluded(meterName, instrumentName))
{
return;
}

string instrumentUnit = null;
string instrumentDescription = null;
string instrumentTags = null;
string meterTags = null;
string meterScopeHash = null;
int? instrumentID = null;

if (traceEvent.Version >= 1)
{
// string instrumentType = (string)traceEvent.PayloadValue(4);
instrumentUnit = (string)traceEvent.PayloadValue(5);
instrumentDescription = (string)traceEvent.PayloadValue(6);
instrumentTags = (string)traceEvent.PayloadValue(7);
meterTags = (string)traceEvent.PayloadValue(8);
meterScopeHash = (string)traceEvent.PayloadValue(9);
Expand All @@ -292,7 +306,18 @@ private static void HandleBeginInstrumentReporting(TraceEvent traceEvent, Counte
// Many different instruments may all share ID zero we don't want to index them by that ID.
instrumentID = (id != 0) ? id : null;
}
payload = new BeginInstrumentReportingPayload(AddCounterMetadata(meterName, instrumentName, instrumentID, meterTags, instrumentTags, meterScopeHash), traceEvent.TimeStamp);
payload = new BeginInstrumentReportingPayload(
AddCounterMetadata(
meterName,
instrumentName,
instrumentID,
meterTags,
instrumentTags,
meterScopeHash,
providerVersion: meterVersion,
counterUnit: instrumentUnit,
counterDescription: instrumentDescription),
traceEvent.TimeStamp);
}

private static void HandleCounterRate(TraceEvent traceEvent, CounterConfiguration counterConfiguration, out ICounterPayload payload)
Expand Down Expand Up @@ -367,7 +392,7 @@ private static void HandleUpDownCounterValue(TraceEvent traceEvent, CounterConfi
string instrumentName = (string)traceEvent.PayloadValue(3);
string unit = (string)traceEvent.PayloadValue(4);
string tags = (string)traceEvent.PayloadValue(5);
//string rateText = (string)traceEvent.PayloadValue(6); // Not currently using rate for UpDownCounters.
string rateText = (string)traceEvent.PayloadValue(6);
string valueText = (string)traceEvent.PayloadValue(7);
int? id = null;
if (traceEvent.Version >= 2)
Expand All @@ -381,11 +406,11 @@ private static void HandleUpDownCounterValue(TraceEvent traceEvent, CounterConfi
}

CounterMetadata metadata = GetCounterMetadata(meterName, instrumentName, id);
if (double.TryParse(valueText, NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double value))
if (double.TryParse(rateText, NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double rate)
&& double.TryParse(valueText, NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double value))
{
// UpDownCounter reports the value, not the rate - this is different than how Counter behaves.
payload = new UpDownCounterPayload(metadata, null, unit, tags, value, traceEvent.TimeStamp);

payload = new UpDownCounterPayload(metadata, null, unit, tags, rate, value, traceEvent.TimeStamp);
}
else
{
Expand Down Expand Up @@ -413,8 +438,8 @@ private static void HandleHistogram(TraceEvent obj, CounterConfiguration configu
string unit = (string)obj.PayloadValue(4);
string tags = (string)obj.PayloadValue(5);
string quantilesText = (string)obj.PayloadValue(6);
//int count - unused arg 7
//double sum - unused arg 8
int count = (int)obj.PayloadValue(7);
double sum = (double)obj.PayloadValue(8);
int? id = null;
if (obj.Version >= 2)
{
Expand All @@ -429,7 +454,7 @@ private static void HandleHistogram(TraceEvent obj, CounterConfiguration configu
//Note quantiles can be empty.
IList<Quantile> quantiles = ParseQuantiles(quantilesText);
CounterMetadata metadata = GetCounterMetadata(meterName, instrumentName, id);
payload = new AggregatePercentilePayload(metadata, null, unit, tags, quantiles, obj.TimeStamp);
payload = new AggregatePercentilePayload(metadata, null, unit, tags, count, sum, quantiles, obj.TimeStamp);
}

private static void HandleHistogramLimitReached(TraceEvent obj, CounterConfiguration configuration, out ICounterPayload payload)
Expand Down