Skip to content

Support Exposing new Metric properties using EventSource #90417

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

Merged
Merged
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 @@ -34,7 +34,7 @@ namespace System.Diagnostics.Metrics
// it may also not be a win to further pessimize the slow-path (JIT compilation is expensive) to squeeze yet more cycles out of
// the fast path.
// - Allocations per lookup: Any lookup of 3 or fewer labels on the above fast path is allocation free. We have separate
// dictionaries dependending on the number of labels in the list and the dictionary keys are structures representing fixed size
// dictionaries depending on the number of labels in the list and the dictionary keys are structures representing fixed size
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha, that was some interesting spelling I came up with ;p

// lists of strings or objects. For example with two labels the lookup is done in a
// FixedSizeLabelNameDictionary<StringSequence2, ConcurrentDictionary<ObjectSequence2, TAggregator>>
// Above 3 labels we have StringSequenceMany and ObjectSequenceMany which wraps an underlying string[] or object?[] respectively.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Tracing;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
Expand Down Expand Up @@ -144,29 +145,51 @@ public void HistogramValuePublished(string sessionId, string meterName, string?
WriteEvent(6, sessionId, meterName, meterVersion ?? "", instrumentName, unit ?? "", tags, quantiles, count, sum);
}

// Sent when we begin to monitor the value of a intrument, either because new session filter arguments changed subscriptions
// Sent when we begin to monitor the value of a instrument, either because new session filter arguments changed subscriptions
// or because an instrument matching the pre-existing filter has just been created. This event precedes all *MetricPublished events
// for the same named instrument.
[Event(7, Keywords = Keywords.TimeSeriesValues)]
[Event(7, Keywords = Keywords.TimeSeriesValues, Version = 1)]
#if !NET8_0_OR_GREATER
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")]
#endif
public void BeginInstrumentReporting(string sessionId, string meterName, string? meterVersion, string instrumentName, string instrumentType, string? unit, string? description)
public void BeginInstrumentReporting(
string sessionId,
string meterName,
string? meterVersion,
string instrumentName,
string instrumentType,
string? unit,
string? description,
string instrumentTags,
string meterTags,
string meterScopeHash)
{
WriteEvent(7, sessionId, meterName, meterVersion ?? "", instrumentName, instrumentType, unit ?? "", description ?? "");
WriteEvent(7, sessionId, meterName, meterVersion ?? "", instrumentName, instrumentType, unit ?? "", description ?? "",
instrumentTags, meterTags, meterScopeHash);
}

// Sent when we stop monitoring the value of a intrument, either because new session filter arguments changed subscriptions
// Sent when we stop monitoring the value of a instrument, either because new session filter arguments changed subscriptions
// or because the Meter has been disposed.
[Event(8, Keywords = Keywords.TimeSeriesValues)]
[Event(8, Keywords = Keywords.TimeSeriesValues, Version = 1)]
#if !NET8_0_OR_GREATER
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")]
#endif
public void EndInstrumentReporting(string sessionId, string meterName, string? meterVersion, string instrumentName, string instrumentType, string? unit, string? description)
public void EndInstrumentReporting(
string sessionId,
string meterName,
string? meterVersion,
string instrumentName,
string instrumentType,
string? unit,
string? description,
string instrumentTags,
string meterTags,
string meterScopeHash)
{
WriteEvent(8, sessionId, meterName, meterVersion ?? "", instrumentName, instrumentType, unit ?? "", description ?? "");
WriteEvent(8, sessionId, meterName, meterVersion ?? "", instrumentName, instrumentType, unit ?? "", description ?? "",
instrumentTags, meterTags, meterScopeHash);
}

[Event(9, Keywords = Keywords.TimeSeriesValues | Keywords.Messages | Keywords.InstrumentPublishing)]
Expand All @@ -181,14 +204,25 @@ public void InitialInstrumentEnumerationComplete(string sessionId)
WriteEvent(10, sessionId);
}

[Event(11, Keywords = Keywords.InstrumentPublishing)]
[Event(11, Keywords = Keywords.InstrumentPublishing, Version = 1)]
#if !NET8_0_OR_GREATER
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")]
#endif
public void InstrumentPublished(string sessionId, string meterName, string? meterVersion, string instrumentName, string instrumentType, string? unit, string? description)
public void InstrumentPublished(
string sessionId,
string meterName,
string? meterVersion,
string instrumentName,
string instrumentType,
string? unit,
string? description,
string instrumentTags,
string meterTags,
string meterScopeHash)
{
WriteEvent(11, sessionId, meterName, meterVersion ?? "", instrumentName, instrumentType, unit ?? "", description ?? "");
WriteEvent(11, sessionId, meterName, meterVersion ?? "", instrumentName, instrumentType, unit ?? "", description ?? "",
instrumentTags, meterTags, meterScopeHash);
}

[Event(12, Keywords = Keywords.TimeSeriesValues)]
Expand Down Expand Up @@ -403,9 +437,12 @@ public void OnEventCommand(EventCommandEventArgs command)
(i, s) => TransmitMetricValue(i, s, sessionId),
(startIntervalTime, endIntervalTime) => Parent.CollectionStart(sessionId, startIntervalTime, endIntervalTime),
(startIntervalTime, endIntervalTime) => Parent.CollectionStop(sessionId, startIntervalTime, endIntervalTime),
i => Parent.BeginInstrumentReporting(sessionId, i.Meter.Name, i.Meter.Version, i.Name, i.GetType().Name, i.Unit, i.Description),
i => Parent.EndInstrumentReporting(sessionId, i.Meter.Name, i.Meter.Version, i.Name, i.GetType().Name, i.Unit, i.Description),
i => Parent.InstrumentPublished(sessionId, i.Meter.Name, i.Meter.Version, i.Name, i.GetType().Name, i.Unit, i.Description),
i => Parent.BeginInstrumentReporting(sessionId, i.Meter.Name, i.Meter.Version, i.Name, i.GetType().Name, i.Unit, i.Description,
FormatTags(i.Tags), FormatTags(i.Meter.Tags), FormatScopeHash(i.Meter.Scope)),
i => Parent.EndInstrumentReporting(sessionId, i.Meter.Name, i.Meter.Version, i.Name, i.GetType().Name, i.Unit, i.Description,
FormatTags(i.Tags), FormatTags(i.Meter.Tags), FormatScopeHash(i.Meter.Scope)),
i => Parent.InstrumentPublished(sessionId, i.Meter.Name, i.Meter.Version, i.Name, i.GetType().Name, i.Unit, i.Description,
FormatTags(i.Tags), FormatTags(i.Meter.Tags), FormatScopeHash(i.Meter.Scope)),
() => Parent.InitialInstrumentEnumerationComplete(sessionId),
e => Parent.Error(sessionId, e.ToString()),
() => Parent.TimeSeriesLimitReached(sessionId),
Expand Down Expand Up @@ -648,6 +685,39 @@ private static void TransmitMetricValue(Instrument instrument, LabeledAggregatio
}
}

private static string FormatScopeHash(object? scope) =>
scope is null ? string.Empty : RuntimeHelpers.GetHashCode(scope).ToString(CultureInfo.InvariantCulture);

private static string FormatTags(IEnumerable<KeyValuePair<string, object?>>? tags)
{
if (tags is null)
{
return string.Empty;
}

StringBuilder sb = new StringBuilder();
bool first = true;
foreach (KeyValuePair<string, object?> tag in tags)
{
if (first)
{
first = false;
}
else
{
sb.Append(',');
}

sb.Append(tag.Key).Append('=');

if (tag.Value is not null)
{
sb.Append(tag.Value.ToString());
}
}
return sb.ToString();
}

private static string FormatTags(KeyValuePair<string, string>[] labels)
{
StringBuilder sb = new StringBuilder();
Expand Down
Loading