Skip to content

Commit

Permalink
[Dotnet Monitor] Add Metadata Support For CounterPayload (#3498)
Browse files Browse the repository at this point in the history
  • Loading branch information
kkeirstead authored Nov 28, 2022
1 parent ac9b0dc commit b734886
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,28 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Collections.ObjectModel;

namespace Microsoft.Diagnostics.Monitoring.EventPipe
{
internal class CounterPayload : ICounterPayload
{
#if NETSTANDARD
private static readonly IReadOnlyDictionary<string, string> Empty = new ReadOnlyDictionary<string, string>(new Dictionary<string, string>(0));
#else
private static readonly IReadOnlyDictionary<string, string> Empty = System.Collections.Immutable.ImmutableDictionary<string, string>.Empty;
#endif

public CounterPayload(DateTime timestamp,
string provider,
string name,
string displayName,
string unit,
double value,
CounterType counterType,
float interval)
float interval,
Dictionary<string, string> metadata)
{
Timestamp = timestamp;
Name = name;
Expand All @@ -28,6 +34,7 @@ public CounterPayload(DateTime timestamp,
CounterType = counterType;
Provider = provider;
Interval = interval;
Metadata = metadata ?? Empty;
}

public string Namespace { get; }
Expand All @@ -47,5 +54,7 @@ public CounterPayload(DateTime timestamp,
public CounterType CounterType { get; }

public string Provider { get; }

public IReadOnlyDictionary<string, string> Metadata { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,7 @@ internal interface ICounterPayload
DateTime Timestamp { get; }

float Interval { get; }

IReadOnlyDictionary<string, string> Metadata { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public static bool TryGetCounterPayload(this TraceEvent traceEvent, CounterFilte
string series = payloadFields["Series"].ToString();
string counterName = payloadFields["Name"].ToString();

Dictionary<string, string> metadataDict = GetMetadata(payloadFields["Metadata"].ToString());

//CONSIDER
//Concurrent counter sessions do not each get a separate interval. Instead the payload
//for _all_ the counters changes the Series to be the lowest specified interval, on a per provider basis.
Expand Down Expand Up @@ -62,13 +64,56 @@ public static bool TryGetCounterPayload(this TraceEvent traceEvent, CounterFilte
displayUnits,
value,
counterType,
intervalSec);
intervalSec,
metadataDict);
return true;
}

return false;
}

//The metadata payload is formatted as a string of comma separated key:value pairs.
//This limitation means that metadata values cannot include commas; otherwise, the
//metadata will be parsed incorrectly. If a value contains a comma, then all metadata
//is treated as invalid and excluded from the payload.
internal static Dictionary<string, string> GetMetadata(string metadataPayload)
{
var metadataDict = new Dictionary<string, string>();

ReadOnlySpan<char> metadata = metadataPayload;

while (!metadata.IsEmpty)
{
int commaIndex = metadata.IndexOf(',');

ReadOnlySpan<char> kvPair;

if (commaIndex < 0)
{
kvPair = metadata;
metadata = default;
}
else
{
kvPair = metadata[..commaIndex];
metadata = metadata.Slice(commaIndex + 1);
}

int colonIndex = kvPair.IndexOf(':');
if (colonIndex < 0)
{
metadataDict.Clear();
break;
}

string metadataKey = kvPair[..colonIndex].ToString();
string metadataValue = kvPair.Slice(colonIndex + 1).ToString();
metadataDict[metadataKey] = metadataValue;
}

return metadataDict;
}

private static int GetInterval(string series)
{
const string comparison = "Interval=";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -484,8 +484,40 @@ public ICounterPayload CreateNext(double value)
EventCounterConstants.CpuUsageUnits,
value,
CounterType.Metric,
actualInterval);
actualInterval,
null);
}
}

/// <summary>
/// Validates that metadata from TraceEvent payloads is parsed correctly.
/// </summary>
[Fact]
public void ValidateMetadataParsing_Success()
{
const string key1 = "K1";
const string value1 = "V1";
const string key2 = "K2";
const string value2 = "V:2";
Dictionary<string, string> metadataDict = TraceEventExtensions.GetMetadata($"{key1}:{value1},{key2}:{value2}");

Assert.Equal(2, metadataDict.Count);
Assert.Equal(value1, metadataDict[key1]);
Assert.Equal(value2, metadataDict[key2]);
}

/// <summary>
/// Validates that metadata with an invalid format from TraceEvent payloads is handled correctly.
/// </summary>
[Theory]
[InlineData("K1:V,1")]
[InlineData("K,1:V")]
[InlineData("K1")]
public void ValidateMetadataParsing_Failure(string invalidMetadata)
{
Dictionary<string, string> metadataDict = TraceEventExtensions.GetMetadata(invalidMetadata);

Assert.Empty(metadataDict);
}
}
}

0 comments on commit b734886

Please sign in to comment.