Merge pull request #299 from Microsoft/abaranch/maskedProps
Allow all characters in property names. Change sanitization to optimi…
This commit is contained in:
Коммит
b99174cdde
|
@ -6,6 +6,7 @@ This changelog will be used to generate documentation on [release notes page](ht
|
|||
|
||||
- InMemoryChannel has a new override for Flush method that accepts timeout.
|
||||
- Local storage folder name was changed. That means that when the application stopped, and the application was updated to the new SDK, then the telemetry from the old local folder will not be send.
|
||||
- Allow all characters in property names and measurements names.
|
||||
|
||||
## Version 2.2.0-beta1
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
Assert.Equal(3, telemetry.Properties.Count); //AvailabilityTelemetry sanitize already sets one property which is why this is 3 instead of 2
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength), telemetry.Properties.Keys.ToArray()[0]);
|
||||
Assert.Equal(new string('X', Property.MaxValueLength), telemetry.Properties.Values.ToArray()[0]);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength - 3) + "001", telemetry.Properties.Keys.ToArray()[1]);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength - 3) + "1", telemetry.Properties.Keys.ToArray()[1]);
|
||||
Assert.Equal(new string('X', Property.MaxValueLength), telemetry.Properties.Values.ToArray()[1]);
|
||||
|
||||
Assert.Same(telemetry.Properties, telemetry.Properties);
|
||||
|
|
|
@ -145,7 +145,7 @@
|
|||
Assert.Equal(2, telemetry.Properties.Count);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength), telemetry.Properties.Keys.ToArray()[0]);
|
||||
Assert.Equal(new string('X', Property.MaxValueLength), telemetry.Properties.Values.ToArray()[0]);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength - 3) + "001", telemetry.Properties.Keys.ToArray()[1]);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength - 3) + "1", telemetry.Properties.Keys.ToArray()[1]);
|
||||
Assert.Equal(new string('X', Property.MaxValueLength), telemetry.Properties.Values.ToArray()[1]);
|
||||
|
||||
Assert.Same(telemetry.Properties, telemetry.Properties);
|
||||
|
|
|
@ -101,13 +101,13 @@
|
|||
string[] values = telemetry.Properties.Values.OrderBy(s => s).ToArray();
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength), keys[1]);
|
||||
Assert.Equal(new string('X', Property.MaxValueLength), values[1]);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength - 3) + "001", keys[0]);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength - 3) + "1", keys[0]);
|
||||
Assert.Equal(new string('X', Property.MaxValueLength), values[0]);
|
||||
|
||||
Assert.Equal(2, telemetry.Metrics.Count);
|
||||
keys = telemetry.Metrics.Keys.OrderBy(s => s).ToArray();
|
||||
Assert.Equal(new string('Y', Property.MaxDictionaryNameLength), keys[1]);
|
||||
Assert.Equal(new string('Y', Property.MaxDictionaryNameLength - 3) + "001", keys[0]);
|
||||
Assert.Equal(new string('Y', Property.MaxDictionaryNameLength - 3) + "1", keys[0]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
|
|
@ -439,7 +439,7 @@
|
|||
Assert.Equal(2, telemetry.Properties.Count);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength), telemetry.Properties.Keys.ToArray()[0]);
|
||||
Assert.Equal(new string('X', Property.MaxValueLength), telemetry.Properties.Values.ToArray()[0]);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength - 3) + "001", telemetry.Properties.Keys.ToArray()[1]);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength - 3) + "1", telemetry.Properties.Keys.ToArray()[1]);
|
||||
Assert.Equal(new string('X', Property.MaxValueLength), telemetry.Properties.Values.ToArray()[1]);
|
||||
}
|
||||
|
||||
|
@ -455,7 +455,7 @@
|
|||
Assert.Equal(2, telemetry.Metrics.Count);
|
||||
string[] keys = telemetry.Metrics.Keys.OrderBy(s => s).ToArray();
|
||||
Assert.Equal(new string('Y', Property.MaxDictionaryNameLength), keys[1]);
|
||||
Assert.Equal(new string('Y', Property.MaxDictionaryNameLength - 3) + "001", keys[0]);
|
||||
Assert.Equal(new string('Y', Property.MaxDictionaryNameLength - 3) + "1", keys[0]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
|
|
@ -136,7 +136,7 @@
|
|||
Assert.Equal(2, telemetry.Properties.Count);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength), telemetry.Properties.Keys.ToArray()[0]);
|
||||
Assert.Equal(new string('X', Property.MaxValueLength), telemetry.Properties.Values.ToArray()[0]);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength - 3) + "001", telemetry.Properties.Keys.ToArray()[1]);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength - 3) + "1", telemetry.Properties.Keys.ToArray()[1]);
|
||||
Assert.Equal(new string('X', Property.MaxValueLength), telemetry.Properties.Values.ToArray()[1]);
|
||||
}
|
||||
|
||||
|
|
|
@ -94,13 +94,13 @@
|
|||
string[] values = telemetry.Properties.Values.OrderBy(s => s).ToArray();
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength), keys[1]);
|
||||
Assert.Equal(new string('X', Property.MaxValueLength), values[1]);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength - 3) + "001", keys[0]);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength - 3) + "1", keys[0]);
|
||||
Assert.Equal(new string('X', Property.MaxValueLength), values[0]);
|
||||
|
||||
Assert.Equal(2, telemetry.Metrics.Count);
|
||||
keys = telemetry.Metrics.Keys.OrderBy(s => s).ToArray();
|
||||
Assert.Equal(new string('Y', Property.MaxDictionaryNameLength), keys[1]);
|
||||
Assert.Equal(new string('Y', Property.MaxDictionaryNameLength - 3) + "001", keys[0]);
|
||||
Assert.Equal(new string('Y', Property.MaxDictionaryNameLength - 3) + "1", keys[0]);
|
||||
|
||||
Assert.Equal(new Uri("http://foo.com/" + new string('Y', Property.MaxUrlLength - 15)), telemetry.Url);
|
||||
}
|
||||
|
|
|
@ -156,13 +156,13 @@
|
|||
string[] values = telemetry.Properties.Values.OrderBy(s => s).ToArray();
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength), keys[1]);
|
||||
Assert.Equal(new string('X', Property.MaxValueLength), values[1]);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength - 3) + "001", keys[0]);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength - 3) + "1", keys[0]);
|
||||
Assert.Equal(new string('X', Property.MaxValueLength), values[0]);
|
||||
|
||||
Assert.Equal(2, telemetry.Metrics.Count);
|
||||
keys = telemetry.Metrics.Keys.OrderBy(s => s).ToArray();
|
||||
Assert.Equal(new string('Y', Property.MaxDictionaryNameLength), keys[1]);
|
||||
Assert.Equal(new string('Y', Property.MaxDictionaryNameLength - 3) + "001", keys[0]);
|
||||
Assert.Equal(new string('Y', Property.MaxDictionaryNameLength - 3) + "1", keys[0]);
|
||||
|
||||
Assert.Equal(new Uri("http://foo.com/" + new string('Y', Property.MaxUrlLength - 15)), telemetry.Url);
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@
|
|||
Assert.Equal(2, telemetry.Properties.Count);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength), telemetry.Properties.Keys.ToArray()[0]);
|
||||
Assert.Equal(new string('X', Property.MaxValueLength), telemetry.Properties.Values.ToArray()[0]);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength - 3) + "001", telemetry.Properties.Keys.ToArray()[1]);
|
||||
Assert.Equal(new string('X', Property.MaxDictionaryNameLength - 3) + "1", telemetry.Properties.Keys.ToArray()[1]);
|
||||
Assert.Equal(new string('X', Property.MaxValueLength), telemetry.Properties.Values.ToArray()[1]);
|
||||
}
|
||||
|
||||
|
|
|
@ -153,37 +153,6 @@
|
|||
Assert.Equal("required", dictionary.Single().Key);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SanitizePropertiesReplacesSpecialCharactersWithUnderscores()
|
||||
{
|
||||
foreach (char invalidCharacter in GetInvalidNameCharacters())
|
||||
{
|
||||
string originalKey = "test" + invalidCharacter + "key";
|
||||
const string OriginalValue = "Test Value";
|
||||
var original = new Dictionary<string, string> { { originalKey, OriginalValue } };
|
||||
|
||||
original.SanitizeProperties();
|
||||
|
||||
string sanitizedKey = originalKey.Replace(invalidCharacter, '_');
|
||||
Assert.Equal(new[] { new KeyValuePair<string, string>(sanitizedKey, OriginalValue) }, original);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SanitizePropertiesMakesKeyUniqueAfterReplacingSpecialCharactersWithUnderscores()
|
||||
{
|
||||
string originalKey = "test#key";
|
||||
var dictionary = new Dictionary<string, string>
|
||||
{
|
||||
{ originalKey, string.Empty },
|
||||
{ originalKey.Replace("#", "_"), string.Empty },
|
||||
};
|
||||
|
||||
dictionary.SanitizeProperties();
|
||||
|
||||
Assert.Contains("test_key001", dictionary.Keys);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SanitizePropertiesTruncatesKeysLongerThan150Characters()
|
||||
{
|
||||
|
@ -253,22 +222,6 @@
|
|||
Assert.Equal(new[] { new KeyValuePair<string, double>(sanitizedKey, OriginalValue) }, original);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SanitizeMeasurementsReplacesSpecialCharactersWithUnderscores()
|
||||
{
|
||||
foreach (char invalidCharacter in GetInvalidNameCharacters())
|
||||
{
|
||||
string originalKey = "test" + invalidCharacter + "key";
|
||||
const double OriginalValue = 42.0;
|
||||
var original = new Dictionary<string, double> { { originalKey, OriginalValue } };
|
||||
|
||||
original.SanitizeMeasurements();
|
||||
|
||||
string sanitizedKey = originalKey.Replace(invalidCharacter, '_');
|
||||
Assert.Equal(new[] { new KeyValuePair<string, double>(sanitizedKey, OriginalValue) }, original);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SanitizeMeasurementsTruncatesKeysLongerThan150Characters()
|
||||
{
|
||||
|
|
|
@ -8,8 +8,7 @@ namespace Microsoft.ApplicationInsights.Extensibility.Implementation
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A helper class for implementing properties of telemetry and context classes.
|
||||
/// </summary>
|
||||
|
@ -27,15 +26,6 @@ namespace Microsoft.ApplicationInsights.Extensibility.Implementation
|
|||
public const int MaxRunLocationLength = 2024;
|
||||
public const int MaxAvailabilityMessageLength = 8192;
|
||||
|
||||
private const RegexOptions SanitizeOptions =
|
||||
#if CORE_PCL
|
||||
RegexOptions.None;
|
||||
#else
|
||||
RegexOptions.Compiled;
|
||||
#endif
|
||||
|
||||
private static readonly Regex InvalidNameCharacters = new Regex(@"[^0-9a-zA-Z-._()\/ ]", Property.SanitizeOptions);
|
||||
|
||||
public static void Set<T>(ref T property, T value) where T : class
|
||||
{
|
||||
if (value == default(T))
|
||||
|
@ -134,16 +124,24 @@ namespace Microsoft.ApplicationInsights.Extensibility.Implementation
|
|||
{
|
||||
if (dictionary != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, string> entry in dictionary.ToArray())
|
||||
{
|
||||
// remove the key from the dictionary first
|
||||
dictionary.Remove(entry.Key);
|
||||
var sanitizedEntries = new Dictionary<string, KeyValuePair<string, string>>(dictionary.Count);
|
||||
|
||||
string sanitizedKey = SanitizeKey(entry.Key, dictionary);
|
||||
foreach (KeyValuePair<string, string> entry in dictionary)
|
||||
{
|
||||
string sanitizedKey = SanitizeKey(entry.Key);
|
||||
string sanitizedValue = SanitizeValue(entry.Value);
|
||||
|
||||
// add it back (sanitized at this point).
|
||||
dictionary.Add(sanitizedKey, sanitizedValue);
|
||||
if ((string.CompareOrdinal(sanitizedKey, entry.Key) != 0) || (string.CompareOrdinal(sanitizedValue, entry.Value) != 0))
|
||||
{
|
||||
sanitizedEntries.Add(entry.Key, new KeyValuePair<string, string>(sanitizedKey, sanitizedValue));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, KeyValuePair<string, string>> entry in sanitizedEntries)
|
||||
{
|
||||
dictionary.Remove(entry.Key);
|
||||
string uniqueKey = MakeKeyUnique(entry.Value.Key, dictionary);
|
||||
dictionary.Add(uniqueKey, entry.Value.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -152,16 +150,26 @@ namespace Microsoft.ApplicationInsights.Extensibility.Implementation
|
|||
{
|
||||
if (dictionary != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, double> entry in dictionary.ToArray())
|
||||
{
|
||||
// remove the key from the dictionary first
|
||||
dictionary.Remove(entry.Key);
|
||||
var sanitizedEntries = new Dictionary<string, KeyValuePair<string, double>>(dictionary.Count);
|
||||
|
||||
string sanitizedKey = SanitizeKey(entry.Key, dictionary);
|
||||
double sanitizeValue = Utils.SanitizeNanAndInfinity(entry.Value);
|
||||
|
||||
// add it back (sanitized at this point).
|
||||
dictionary.Add(sanitizedKey, sanitizeValue);
|
||||
foreach (KeyValuePair<string, double> entry in dictionary)
|
||||
{
|
||||
string sanitizedKey = SanitizeKey(entry.Key);
|
||||
|
||||
bool valueChanged;
|
||||
double sanitizedValue = Utils.SanitizeNanAndInfinity(entry.Value, out valueChanged);
|
||||
|
||||
if ((string.CompareOrdinal(sanitizedKey, entry.Key) != 0) || valueChanged)
|
||||
{
|
||||
sanitizedEntries.Add(entry.Key, new KeyValuePair<string, double>(sanitizedKey, sanitizedValue));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, KeyValuePair<string, double>> entry in sanitizedEntries)
|
||||
{
|
||||
dictionary.Remove(entry.Key);
|
||||
string uniqueKey = MakeKeyUnique(entry.Value.Key, dictionary);
|
||||
dictionary.Add(uniqueKey, entry.Value.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -182,13 +190,10 @@ namespace Microsoft.ApplicationInsights.Extensibility.Implementation
|
|||
return value.Length > maxLength ? value.Substring(0, maxLength) : value;
|
||||
}
|
||||
|
||||
private static string SanitizeKey<TValue>(string key, IDictionary<string, TValue> dictionary)
|
||||
private static string SanitizeKey(string key)
|
||||
{
|
||||
string sanitizedKey = TrimAndTruncate(key, Property.MaxDictionaryNameLength);
|
||||
sanitizedKey = InvalidNameCharacters.Replace(sanitizedKey, "_");
|
||||
sanitizedKey = MakeKeyNonEmpty(sanitizedKey);
|
||||
sanitizedKey = MakeKeyUnique(sanitizedKey, dictionary);
|
||||
return sanitizedKey;
|
||||
return MakeKeyNonEmpty(sanitizedKey);
|
||||
}
|
||||
|
||||
private static string MakeKeyNonEmpty(string key)
|
||||
|
@ -205,8 +210,8 @@ namespace Microsoft.ApplicationInsights.Extensibility.Implementation
|
|||
int candidate = 1;
|
||||
do
|
||||
{
|
||||
key = truncatedKey + candidate.ToString(CultureInfo.InvariantCulture).PadLeft(UniqueNumberLength, '0');
|
||||
candidate++;
|
||||
key = truncatedKey + candidate;
|
||||
++candidate;
|
||||
}
|
||||
while (dictionary.ContainsKey(key));
|
||||
}
|
||||
|
|
|
@ -90,10 +90,19 @@
|
|||
|
||||
public static double SanitizeNanAndInfinity(double value)
|
||||
{
|
||||
bool valueChanged;
|
||||
return SanitizeNanAndInfinity(value, out valueChanged);
|
||||
}
|
||||
|
||||
public static double SanitizeNanAndInfinity(double value, out bool valueChanged)
|
||||
{
|
||||
valueChanged = false;
|
||||
|
||||
// Disallow Nan and Infinity since Breeze does not accept it
|
||||
if (double.IsInfinity(value) || double.IsNaN(value))
|
||||
{
|
||||
value = 0;
|
||||
valueChanged = true;
|
||||
}
|
||||
|
||||
return value;
|
||||
|
|
Загрузка…
Ссылка в новой задаче