Skip to content

Commit ec14cb7

Browse files
committed
Merge Exception.Data properties into Error.Data properties
This reduces nesting of extended data objects (no more error.data.data)
1 parent 564d6eb commit ec14cb7

File tree

5 files changed

+143
-10
lines changed

5 files changed

+143
-10
lines changed

Source/Extras/Extensions/ToErrorModelExtensions.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace Exceptionless.Extras {
1919
internal static class ToErrorModelExtensions {
2020
private static readonly ConcurrentDictionary<string, Module> _moduleCache = new ConcurrentDictionary<string, Module>();
2121
private static readonly string[] _exceptionExclusions = {
22-
"HelpLink", "ExceptionContext", "InnerExceptions", "InnerException", "Errors", "Types",
22+
"@exceptionless", "Data", "HelpLink", "ExceptionContext", "InnerExceptions", "InnerException", "Errors", "Types",
2323
"Message", "Source", "StackTrace", "TargetSite", "HResult",
2424
"Entries", "StateEntries", "PersistedState", "Results"
2525
};
@@ -68,8 +68,22 @@ private static Error ToErrorModelInternal(Exception exception, ExceptionlessClie
6868
log.Error(typeof(ExceptionlessClient), ex, "Error populating TargetMethod: " + ex.Message);
6969
}
7070

71+
var exclusions = _exceptionExclusions.Union(client.Configuration.DataExclusions).ToList();
72+
try {
73+
if (exception.Data != null) {
74+
foreach (object k in exception.Data.Keys) {
75+
string key = k != null ? k.ToString() : null;
76+
if (String.IsNullOrEmpty(key) || key.AnyWildcardMatches(exclusions, true))
77+
continue;
78+
79+
error.Data[key] = exception.Data[k];
80+
}
81+
}
82+
} catch (Exception ex) {
83+
log.Error(typeof(ExceptionlessClient), ex, "Error populating Data: " + ex.Message);
84+
}
85+
7186
try {
72-
var exclusions = _exceptionExclusions.Union(client.Configuration.DataExclusions);
7387
var extraProperties = type.GetPublicProperties().Where(p => !p.Name.AnyWildcardMatches(exclusions, true)).ToDictionary(p => p.Name, p => {
7488
try {
7589
return p.GetValue(exception, null);

Source/Shared/Extensions/ExceptionExtensions.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,10 @@ public static void MarkProcessed(this Exception exception) {
6363
try {
6464
if (exception.Data != null) {
6565
var genericTypes = exception.Data.GetType().GetGenericArguments();
66-
exception.Data[_marker] = genericTypes.Length > 0 ? genericTypes[0].GetDefaultValue() : null;
66+
if (genericTypes.Length == 0 || genericTypes[0].IsAssignableFrom(typeof(string)))
67+
exception.Data[_marker] = genericTypes.Length > 0 ? genericTypes[1].GetDefaultValue() : null;
6768
}
68-
} catch (Exception) { }
69+
} catch (Exception) {}
6970

7071
MarkProcessed(exception.InnerException);
7172
}

Source/Shared/Extensions/ToSimpleErrorModelExtensions.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
using System;
22
using System.Collections;
3-
using System.Collections.Generic;
43
using System.Linq;
4+
using Exceptionless.Dependency;
55
using Exceptionless.Logging;
66
using Exceptionless.Models.Data;
77

88
namespace Exceptionless.Extensions {
99
internal static class ToSimpleErrorModelExtensions {
1010
private static readonly string[] _exceptionExclusions = {
11-
"HelpLink", "ExceptionContext", "InnerExceptions", "InnerException", "Errors", "Types",
11+
"@exceptionless", "Data", "HelpLink", "ExceptionContext", "InnerExceptions", "InnerException", "Errors", "Types",
1212
"Message", "Source", "StackTrace", "TargetSite", "HResult",
1313
"Entries", "StateEntries", "PersistedState", "Results"
1414
};
@@ -25,6 +25,7 @@ public static SimpleError ToSimpleErrorModel(this Exception exception, Exception
2525
if (client == null)
2626
client = ExceptionlessClient.Default;
2727

28+
var log = client.Configuration.Resolver.GetLog();
2829
Type type = exception.GetType();
2930

3031
var error = new SimpleError {
@@ -33,8 +34,22 @@ public static SimpleError ToSimpleErrorModel(this Exception exception, Exception
3334
StackTrace = exception.StackTrace
3435
};
3536

37+
var exclusions = _exceptionExclusions.Union(client.Configuration.DataExclusions).ToList();
38+
try {
39+
if (exception.Data != null) {
40+
foreach (object k in exception.Data.Keys) {
41+
string key = k != null ? k.ToString() : null;
42+
if (String.IsNullOrEmpty(key) || key.AnyWildcardMatches(exclusions, true))
43+
continue;
44+
45+
error.Data[key] = exception.Data[k];
46+
}
47+
}
48+
} catch (Exception ex) {
49+
log.Error(typeof(ExceptionlessClient), ex, "Error populating Data: " + ex.Message);
50+
}
51+
3652
try {
37-
var exclusions = _exceptionExclusions.Union(client.Configuration.DataExclusions);
3853
var extraProperties = type.GetPublicProperties().Where(p => !p.Name.AnyWildcardMatches(exclusions, true)).ToDictionary(p => p.Name, p => {
3954
try {
4055
return p.GetValue(exception, null);

Source/Shared/Extensions/TypeExtensions.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ public static PropertyInfo[] GetPublicProperties(this Type type) {
3939
public static object GetDefaultValue(this Type type) {
4040
if (type == null || type.IsNullable())
4141
return null;
42-
42+
43+
if (type == typeof(string))
44+
return default(string);
4345
if (type == typeof(bool))
4446
return default(bool);
4547
if (type == typeof(byte))
@@ -65,6 +67,9 @@ public static object GetDefaultValue(this Type type) {
6567
if (type == typeof(ushort))
6668
return default(ushort);
6769

70+
if (type.IsClass || type.IsInterface)
71+
return null;
72+
6873
return Activator.CreateInstance(type);
6974
}
7075

Source/Tests/Plugins/PluginTests.cs

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections;
23
using System.Collections.Generic;
34
using System.Linq;
45
using System.Threading.Tasks;
@@ -89,7 +90,90 @@ public void ErrorPlugin_DiscardDuplicates() {
8990
Assert.Null(error);
9091
}
9192
}
92-
93+
94+
public static IEnumerable<object[]> DifferentExceptionDataDictionaryTypes {
95+
get {
96+
return new[] {
97+
new object[] { null, false, 0 },
98+
new object[] { new Dictionary<object, object> { { (object)1, (object)1 } }, true, 1 },
99+
new object[] { new Dictionary<PriorityAttribute, PriorityAttribute>() { { new PriorityAttribute(1), new PriorityAttribute(1) } }, false, 1 },
100+
new object[] { new Dictionary<int, int> { { 1, 1 } }, false, 1 },
101+
new object[] { new Dictionary<bool, bool> { { false, false } }, false, 1 },
102+
new object[] { new Dictionary<Guid, Guid> { { Guid.Empty, Guid.Empty } }, false, 1 },
103+
new object[] { new Dictionary<IData, IData> { { new SimpleError(), new SimpleError() } }, false, 1 },
104+
new object[] { new Dictionary<TestEnum, TestEnum> { { TestEnum.None, TestEnum.None } }, false, 1 },
105+
new object[] { new Dictionary<TestStruct, TestStruct> { { new TestStruct(), new TestStruct() } }, false, 1 },
106+
new object[] { new Dictionary<string, string> { { "test", "string" } }, true, 1 },
107+
new object[] { new Dictionary<string, object> { { "test", "object" } }, true, 1 },
108+
new object[] { new Dictionary<string, PriorityAttribute> { { "test", new PriorityAttribute(1) } }, true, 1 },
109+
new object[] { new Dictionary<string, Guid> { { "test", Guid.Empty } }, true, 1 },
110+
new object[] { new Dictionary<string, IData> { { "test", new SimpleError() } }, true, 1 },
111+
new object[] { new Dictionary<string, TestEnum> { { "test", TestEnum.None } }, true, 1 },
112+
new object[] { new Dictionary<string, TestStruct> { { "test", new TestStruct() } }, true, 1 },
113+
new object[] { new Dictionary<string, int> { { "test", 1 } }, true, 1 },
114+
new object[] { new Dictionary<string, bool> { { "test", false } }, true, 1 }
115+
};
116+
}
117+
}
118+
119+
[Theory]
120+
[MemberData("DifferentExceptionDataDictionaryTypes")]
121+
public void ErrorPlugin_CanProcessDifferentExceptionDataDictionaryTypes(IDictionary data, bool canMarkAsProcessed, int processedDataItemCount) {
122+
var errorPlugins = new List<IEventPlugin> {
123+
new ErrorPlugin(),
124+
new SimpleErrorPlugin()
125+
};
126+
127+
foreach (var plugin in errorPlugins) {
128+
if (data != null && data.Contains("@exceptionless"))
129+
data.Remove("@exceptionless");
130+
131+
var exception = new MyApplicationException("Test") { SetsDataProperty = data };
132+
var client = new ExceptionlessClient();
133+
client.Configuration.AddDataExclusions("SetsDataProperty");
134+
var context = new EventPluginContext(client, new Event());
135+
context.ContextData.SetException(exception);
136+
plugin.Run(context);
137+
Assert.False(context.Cancel);
138+
139+
Assert.Equal(canMarkAsProcessed, exception.Data != null && exception.Data.Contains("@exceptionless"));
140+
141+
IData error = context.Event.GetError() as IData ?? context.Event.GetSimpleError();
142+
Assert.NotNull(error);
143+
Assert.Equal(processedDataItemCount, error.Data.Count);
144+
}
145+
}
146+
147+
[Fact]
148+
public void ErrorPlugin_CopyExceptionDataToRootErrorData() {
149+
var errorPlugins = new List<IEventPlugin> {
150+
new ErrorPlugin(),
151+
new SimpleErrorPlugin()
152+
};
153+
154+
foreach (var plugin in errorPlugins) {
155+
var exception = new MyApplicationException("Test") {
156+
RandomValue = "Test",
157+
SetsDataProperty = new Dictionary<object, string> {
158+
{ 1, 1.GetType().Name },
159+
{ "test", "test".GetType().Name },
160+
{ Guid.NewGuid(), typeof(Guid).Name },
161+
{ false, typeof(bool).Name }
162+
}
163+
};
164+
165+
var client = new ExceptionlessClient();
166+
var context = new EventPluginContext(client, new Event());
167+
context.ContextData.SetException(exception);
168+
plugin.Run(context);
169+
Assert.False(context.Cancel);
170+
171+
IData error = context.Event.GetError() as IData ?? context.Event.GetSimpleError();
172+
Assert.NotNull(error);
173+
Assert.Equal(5, error.Data.Count);
174+
}
175+
}
176+
93177
[Fact]
94178
public void ErrorPlugin_IgnoredProperties() {
95179
var exception = new MyApplicationException("Test") {
@@ -336,13 +420,27 @@ public void Run(EventPluginContext context) {}
336420
public class PluginWithPriority11 : IEventPlugin {
337421
public void Run(EventPluginContext context) {}
338422
}
423+
424+
private enum TestEnum {
425+
None = 1
426+
}
427+
428+
private struct TestStruct {
429+
public int Id { get; set; }
430+
}
339431

340432
public class MyApplicationException : ApplicationException {
341-
public MyApplicationException(string message) : base(message) {}
433+
public MyApplicationException(string message) : base(message) {
434+
SetsDataProperty = Data;
435+
}
342436

343437
public string IgnoredProperty { get; set; }
344438

345439
public string RandomValue { get; set; }
440+
441+
public IDictionary SetsDataProperty { get; set; }
442+
443+
public override IDictionary Data { get { return SetsDataProperty; } }
346444
}
347445
}
348446
}

0 commit comments

Comments
 (0)