Skip to content

Commit

Permalink
Fixed an issue where data exclusion wildcards weren't being checked
Browse files Browse the repository at this point in the history
* We are now checking data exclusion wildcards.
* We are now flowing the ExceptionlessClient through the codebase and
using it to get dependencies that we were not before (Add Object is now
using the proper json serializer.
  • Loading branch information
niemyjski committed Nov 24, 2015
1 parent ef15440 commit 896432e
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 35 deletions.
25 changes: 16 additions & 9 deletions Source/Extras/Extensions/ToErrorModelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Linq;
using System.Reflection;
using System.Security;
using Exceptionless.Dependency;
using Exceptionless.Extensions;
using Exceptionless.Logging;
using Exceptionless.Models;
Expand All @@ -27,13 +28,19 @@ internal static class ToErrorModelExtensions {
/// Sets the properties from an exception.
/// </summary>
/// <param name="exception">The exception to populate properties from.</param>
/// <param name="log">The log implementation used for diagnostic information.</param>
/// <param name="dataExclusions">Data exclusions that get run on extra exception properties.</param>
public static Error ToErrorModel(this Exception exception, IExceptionlessLog log, IEnumerable<string> dataExclusions) {
return ToErrorModelInternal(exception, log, dataExclusions);
/// <param name="client">
/// The ExceptionlessClient instance used for configuration. If a client is not specified, it will use
/// ExceptionlessClient.Default.
/// </param>
public static Error ToErrorModel(this Exception exception, ExceptionlessClient client = null) {
if (client == null)
client = ExceptionlessClient.Default;

return ToErrorModelInternal(exception, client);
}

private static Error ToErrorModelInternal(Exception exception, IExceptionlessLog log, IEnumerable<string> dataExclusions, bool isInner = false) {
private static Error ToErrorModelInternal(Exception exception, ExceptionlessClient client, bool isInner = false) {
var log = client.Configuration.Resolver.GetLog();
Type type = exception.GetType();

var error = new Error {
Expand Down Expand Up @@ -62,8 +69,8 @@ private static Error ToErrorModelInternal(Exception exception, IExceptionlessLog
}

try {
var exclusions = _exceptionExclusions.Union(dataExclusions ?? new List<string>());
var extraProperties = type.GetPublicProperties().Where(p => !exclusions.Contains(p.Name)).ToDictionary(p => p.Name, p => {
var exclusions = _exceptionExclusions.Union(client.Configuration.DataExclusions);
var extraProperties = type.GetPublicProperties().Where(p => !p.Name.AnyWildcardMatches(exclusions, true)).ToDictionary(p => p.Name, p => {
try {
return p.GetValue(exception, null);
} catch { }
Expand All @@ -78,12 +85,12 @@ private static Error ToErrorModelInternal(Exception exception, IExceptionlessLog
Name = Error.KnownDataKeys.ExtraProperties,
IgnoreSerializationErrors = true,
MaxDepthToSerialize = 5
});
}, client);
}
} catch { }

if (exception.InnerException != null)
error.Inner = ToErrorModelInternal(exception.InnerException, log, dataExclusions, true);
error.Inner = ToErrorModelInternal(exception.InnerException, client, true);

return error;
}
Expand Down
2 changes: 1 addition & 1 deletion Source/Extras/Plugins/ErrorPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public void Run(EventPluginContext context) {
return;

context.Event.Type = Event.KnownTypes.Error;
context.Event.Data[Event.KnownDataKeys.Error] = exception.ToErrorModel(context.Log, context.Client.Configuration.DataExclusions);
context.Event.Data[Event.KnownDataKeys.Error] = exception.ToErrorModel(context.Client);
}
}
}
28 changes: 19 additions & 9 deletions Source/Samples/SampleMvc/Controllers/HomeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ public ActionResult Index() {

[HttpPost]
public ViewResult Index(SomeModel model) {
throw new ApplicationException("Error on form submit.");
throw new MyApplicationException("Error on form submit.") {
IgnoredProperty = "Index Test",
RandomValue = Guid.NewGuid().ToString()
};
}

[HttpGet]
Expand Down Expand Up @@ -72,15 +75,16 @@ public ActionResult CustomBoom() {
public ActionResult Boom25() {
for (int i = 0; i < 25; i++) {
try {
throw new ApplicationException("Boom!");
throw new MyApplicationException("Boom!") {
IgnoredProperty = "Test",
RandomValue = Guid.NewGuid().ToString()
};
} catch (Exception ex) {
ex.ToExceptionless()
ex.ToExceptionless()
.SetUserIdentity("[email protected]")
.AddRecentTraceLogEntries()
.AddRequestInfo()
.AddObject(new {
Blah = "Hello"
}, name: "Hello")
.AddObject(new { Blah = "Hello" }, name: "Hello")
.AddTags("SomeTag", "AnotherTag")
.MarkAsCritical()
.Submit();
Expand All @@ -92,9 +96,7 @@ public ActionResult Boom25() {
.SetUserDescription("[email protected]", "Some description.")
.AddRecentTraceLogEntries()
.AddRequestInfo()
.AddObject(new {
Blah = "Hello"
}, name: "Hello", excludedPropertyNames: new[] { "Blah" })
.AddObject(new { Blah = "Hello" }, name: "Hello", excludedPropertyNames: new[] { "Blah" })
.AddTags("SomeTag", "AnotherTag")
.MarkAsCritical()
.Submit();
Expand All @@ -110,4 +112,12 @@ public ActionResult CreateRequestValidationException(string value) {
return RedirectToAction("Index");
}
}

public class MyApplicationException : ApplicationException {
public MyApplicationException(string message) : base(message) {}

public string IgnoredProperty { get; set; }

public string RandomValue { get; set; }
}
}
2 changes: 2 additions & 0 deletions Source/Samples/SampleMvc/Global.asax.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public class MvcApplication : HttpApplication {
protected void Application_Start() {
Trace.Listeners.Add(new NLogTraceListener());
//ExceptionlessClient.Default.Log = new NLogExceptionlessLog();
ExceptionlessClient.Default.Configuration.DefaultData["FirstName"] = "Blake";
ExceptionlessClient.Default.Configuration.DefaultData["IgnoredProperty"] = "Some random data";

AreaRegistration.RegisterAllAreas();

Expand Down
18 changes: 12 additions & 6 deletions Source/Shared/Extensions/DataDictionaryExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,14 @@ public static void AddObject(this IData data, object value, string name = null,
Data = value,
Name = name,
MaxDepthToSerialize = maxDepth,
ExcludedPropertyNames = excludedPropertyNames != null ? client.Configuration.DataExclusions.Union(excludedPropertyNames).ToArray() : client.Configuration.DataExclusions.ToArray(),
IgnoreSerializationErrors = ignoreSerializationErrors
};

if (excludedPropertyNames != null)
info.ExcludedPropertyNames.AddRange(excludedPropertyNames);
}

AddObject(data, info);
AddObject(data, info, client);
}

/// <summary>
Expand All @@ -94,13 +96,19 @@ public static void AddObject(this IData data, ExtendedDataInfo info, Exceptionle

if (info == null || info.Data == null)
return;

string[] exclusions = info.ExcludedPropertyNames != null
? client.Configuration.DataExclusions.Union(info.ExcludedPropertyNames).ToArray()
: client.Configuration.DataExclusions.ToArray();

string name = !String.IsNullOrWhiteSpace(info.Name) ? info.Name.Trim() : null;
if (String.IsNullOrEmpty(name)) {
name = info.Data.GetType().Name;
int index = 1;
while (data.Data.ContainsKey(name))
name = info.Data.GetType().Name + index++;
} else if (name.AnyWildcardMatches(exclusions, true)) {
return;
}

Type dataType = info.Data.GetType();
Expand All @@ -118,10 +126,8 @@ public static void AddObject(this IData data, ExtendedDataInfo info, Exceptionle
if (dataType.IsPrimitiveType()) {
json = info.Data.ToString();
} else {
string[] excludedPropertyNames = info.ExcludedPropertyNames != null ? client.Configuration.DataExclusions.Union(info.ExcludedPropertyNames).ToArray() : client.Configuration.DataExclusions.ToArray();

var serializer = DependencyResolver.Default.GetJsonSerializer();
json = serializer.Serialize(info.Data, excludedPropertyNames, info.MaxDepthToSerialize.HasValue ? info.MaxDepthToSerialize.Value : 5, info.IgnoreSerializationErrors);
var serializer = client.Configuration.Resolver.GetJsonSerializer();
json = serializer.Serialize(info.Data, exclusions, info.MaxDepthToSerialize.HasValue ? info.MaxDepthToSerialize.Value : 5, info.IgnoreSerializationErrors);
}
} catch (Exception ex) {
json = ex.ToString();
Expand Down
5 changes: 4 additions & 1 deletion Source/Shared/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public static string ToLowerUnderscoredWords(this string value) {
}

public static bool AnyWildcardMatches(this string value, IEnumerable<string> patternsToMatch, bool ignoreCase = false) {
if (patternsToMatch == null)
if (patternsToMatch == null || value == null)
return false;

if (ignoreCase)
Expand All @@ -33,6 +33,9 @@ public static bool AnyWildcardMatches(this string value, IEnumerable<string> pat
}

private static bool CheckForMatch(string pattern, string value, bool ignoreCase = true) {
if (pattern == null || value == null)
return false;

bool startsWithWildcard = pattern.StartsWith("*");
if (startsWithWildcard)
pattern = pattern.Substring(1);
Expand Down
19 changes: 12 additions & 7 deletions Source/Shared/Extensions/ToSimpleErrorModelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@ internal static class ToSimpleErrorModelExtensions {
/// Sets the properties from an exception.
/// </summary>
/// <param name="exception">The exception to populate properties from.</param>
/// <param name="log">The log implementation used for diagnostic information.</param>
/// <param name="dataExclusions">Data exclusions that get run on extra exception properties.</param>
public static SimpleError ToSimpleErrorModel(this Exception exception, IExceptionlessLog log, IEnumerable<string> dataExclusions) {
/// <param name="client">
/// The ExceptionlessClient instance used for configuration. If a client is not specified, it will use
/// ExceptionlessClient.Default.
/// </param>
public static SimpleError ToSimpleErrorModel(this Exception exception, ExceptionlessClient client = null) {
if (client == null)
client = ExceptionlessClient.Default;

Type type = exception.GetType();

var error = new SimpleError {
Expand All @@ -29,8 +34,8 @@ public static SimpleError ToSimpleErrorModel(this Exception exception, IExceptio
};

try {
var exclusions = _exceptionExclusions.Union(dataExclusions ?? new List<string>());
var extraProperties = type.GetPublicProperties().Where(p => !exclusions.Contains(p.Name)).ToDictionary(p => p.Name, p => {
var exclusions = _exceptionExclusions.Union(client.Configuration.DataExclusions);
var extraProperties = type.GetPublicProperties().Where(p => !p.Name.AnyWildcardMatches(exclusions, true)).ToDictionary(p => p.Name, p => {
try {
return p.GetValue(exception, null);
} catch {}
Expand All @@ -45,12 +50,12 @@ public static SimpleError ToSimpleErrorModel(this Exception exception, IExceptio
Name = SimpleError.KnownDataKeys.ExtraProperties,
IgnoreSerializationErrors = true,
MaxDepthToSerialize = 5
});
}, client);
}
} catch {}

if (exception.InnerException != null)
error.Inner = exception.InnerException.ToSimpleErrorModel(log, dataExclusions);
error.Inner = exception.InnerException.ToSimpleErrorModel(client);

return error;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public void Run(EventPluginContext context) {
context.Event.Tags.Add(tag);

foreach (var data in context.Client.Configuration.DefaultData)
context.Event.SetProperty(data.Key, data.Value, excludedPropertyNames: context.Client.Configuration.DataExclusions);
context.Event.SetProperty(data.Key, data.Value, client: context.Client);
}
}
}
2 changes: 1 addition & 1 deletion Source/Shared/Plugins/Default/SimpleErrorPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public void Run(EventPluginContext context) {
return;

context.Event.Type = Event.KnownTypes.Error;
context.Event.Data[Event.KnownDataKeys.SimpleError] = exception.ToSimpleErrorModel(context.Log, context.Client.Configuration.DataExclusions);
context.Event.Data[Event.KnownDataKeys.SimpleError] = exception.ToSimpleErrorModel(context.Client);
}
}
}
65 changes: 65 additions & 0 deletions Source/Tests/Plugins/PluginTests.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Exceptionless.Dependency;
using Exceptionless.Plugins;
using Exceptionless.Plugins.Default;
using Exceptionless.Models;
using Exceptionless.Models.Data;
using Exceptionless.Tests.Utility;
using Xunit;
using Xunit.Abstractions;
Expand Down Expand Up @@ -38,6 +40,61 @@ public void ConfigurationDefaults_EnsureNoDuplicateTagsOrData() {
}
}

[Fact]
public void ConfigurationDefaults_IgnoredProperties() {
var client = new ExceptionlessClient();
client.Configuration.DefaultData.Add("Message", "Test");

var context = new EventPluginContext(client, new Event());
var plugin = new ConfigurationDefaultsPlugin();
plugin.Run(context);
Assert.Equal(1, context.Event.Data.Count);
Assert.Equal("Test", context.Event.Data["Message"]);

client.Configuration.AddDataExclusions("Ignore*");
client.Configuration.DefaultData.Add("Ignored", "Test");
plugin.Run(context);
Assert.Equal(1, context.Event.Data.Count);
Assert.Equal("Test", context.Event.Data["Message"]);
}

[Fact]
public void ErrorPlugin_IgnoredProperties() {
var exception = new MyApplicationException("Test") {
IgnoredProperty = "Test",
RandomValue = "Test"
};

var errorPlugins = new List<IEventPlugin> {
new ErrorPlugin(),
new SimpleErrorPlugin()
};

foreach (var plugin in errorPlugins) {
var client = new ExceptionlessClient();
var context = new EventPluginContext(client, new Event());
context.ContextData.SetException(exception);

plugin.Run(context);
IData error = context.Event.GetError() as IData ?? context.Event.GetSimpleError();
Assert.NotNull(error);
Assert.True(error.Data.ContainsKey(Error.KnownDataKeys.ExtraProperties));
var json = error.Data[Error.KnownDataKeys.ExtraProperties] as string;
Assert.Equal("{\"ignored_property\":\"Test\",\"random_value\":\"Test\"}", json);

client.Configuration.AddDataExclusions("Ignore*");
context = new EventPluginContext(client, new Event());
context.ContextData.SetException(exception);

plugin.Run(context);
error = context.Event.GetError() as IData ?? context.Event.GetSimpleError();
Assert.NotNull(error);
Assert.True(error.Data.ContainsKey(Error.KnownDataKeys.ExtraProperties));
json = error.Data[Error.KnownDataKeys.ExtraProperties] as string;
Assert.Equal("{\"random_value\":\"Test\"}", json);
}
}

[Theory(Skip = "TODO: This needs to be skipped until the client is sending session start and end.")]
[InlineData(Event.KnownTypes.Error)]
[InlineData(Event.KnownTypes.FeatureUsage)]
Expand Down Expand Up @@ -147,5 +204,13 @@ public void Run(EventPluginContext context) {}
public class PluginWithPriority11 : IEventPlugin {
public void Run(EventPluginContext context) {}
}

public class MyApplicationException : ApplicationException {
public MyApplicationException(string message) : base(message) {}

public string IgnoredProperty { get; set; }

public string RandomValue { get; set; }
}
}
}

0 comments on commit 896432e

Please sign in to comment.