Merge pull request #299 from Microsoft/abaranch/maskedProps

Allow all characters in property names. Change sanitization to optimi…
This commit is contained in:
Anastasia Baranchenkova 2016-09-02 11:25:49 -07:00 коммит произвёл GitHub
Родитель ddd9b875f2 84ec5b190e
Коммит b99174cdde
12 изменённых файлов: 61 добавлений и 93 удалений

Просмотреть файл

@ -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;