-
Notifications
You must be signed in to change notification settings - Fork 4
/
Exceptions.cs
272 lines (245 loc) · 11.1 KB
/
Exceptions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
namespace Menees
{
#region Using Directives
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security;
using System.Text;
using Menees.Diagnostics;
#endregion
/// <summary>
/// Helper methods for creating and logging new exceptions.
/// </summary>
public static class Exceptions
{
#region Public Methods
/// <summary>
/// Creates and logs a new ArgumentException with the specified message.
/// </summary>
/// <param name="message">The error message.</param>
/// <returns>A new exception instance.</returns>
public static ArgumentException NewArgumentException(string message) => NewArgumentException(message, null, null);
/// <summary>
/// Creates and logs a new ArgumentException with the specified message and argument name.
/// </summary>
/// <param name="message">The error message.</param>
/// <param name="argName">The argument name.</param>
/// <returns>A new exception instance.</returns>
public static ArgumentException NewArgumentException(string message, string? argName) => NewArgumentException(message, argName, null);
/// <summary>
/// Creates and logs a new ArgumentException with the specified message and context properties.
/// </summary>
/// <param name="message">The error message.</param>
/// <param name="contextProperties">Context information to log with the exception.</param>
/// <returns>A new exception instance.</returns>
public static ArgumentException NewArgumentException(string message, IDictionary<string, object>? contextProperties)
=> NewArgumentException(message, null, contextProperties);
/// <summary>
/// Creates and logs a new ArgumentException with the specified message, argument name, and context properties.
/// </summary>
/// <param name="message">The error message.</param>
/// <param name="argName">The argument name.</param>
/// <param name="contextProperties">Context information to log with the exception.</param>
/// <returns>A new exception instance.</returns>
public static ArgumentException NewArgumentException(string message, string? argName, IDictionary<string, object>? contextProperties)
=> Log(new ArgumentException(message, argName), contextProperties);
/// <summary>
/// Creates and logs a new ArgumentNullException.
/// </summary>
/// <param name="argName">The argument name.</param>
/// <returns>A new exception instance.</returns>
public static ArgumentNullException NewArgumentNullException(string argName) => Log(new ArgumentNullException(argName));
/// <summary>
/// Creates and logs a new ArgumentNullException with the specified context properties.
/// </summary>
/// <param name="argName">The argument name.</param>
/// <param name="contextProperties">Context information to log with the exception.</param>
/// <returns>A new exception instance.</returns>
public static ArgumentNullException NewArgumentNullException(string argName, IDictionary<string, object>? contextProperties)
=> Log(new ArgumentNullException(argName), contextProperties);
/// <summary>
/// Logs the given exception as an error.
/// </summary>
/// <typeparam name="T">The type of exception to log and return.</typeparam>
/// <param name="ex">The exception to log.
/// <para/>Note: The <see cref="Exception.Data"/> dictionary
/// may get modified for this exception.</param>
/// <returns>The exception passed in as <paramref name="ex"/>.</returns>
/// <remarks>
/// See <see cref="Log<T>(T, Type)"/> for an example and more information.
/// </remarks>
public static T Log<T>(T ex)
where T : Exception => Log(ex, null, null);
/// <summary>
/// Logs the given exception as an error.
/// </summary>
/// <typeparam name="T">The type of exception to log and return.</typeparam>
/// <param name="ex">The exception to log.
/// <para/>Note: The <see cref="Exception.Data"/> dictionary
/// may get modified for this exception.</param>
/// <param name="category">The configuration category, which is typically the caller's type.</param>
/// <returns>The exception passed in as <paramref name="ex"/>.</returns>
/// <remarks>
/// Other methods in this class should be used if you're creating one of the following "typical"
/// exceptions:
/// <list type="table">
/// <listheader>
/// <term>Exception Type</term>
/// <description>Method</description>
/// </listheader>
/// <item>
/// <term>ArgumentException</term>
/// <description><see cref="NewArgumentException(string, string)"/></description>
/// </item>
/// <item>
/// <term>ArgumentNullException</term>
/// <description><see cref="NewArgumentNullException(string)"/></description>
/// </item>
/// <item>
/// <term>InvalidOperationException</term>
/// <description><see cref="NewInvalidOperationException(string)"/></description>
/// </item>
/// </list>
/// </remarks>
/// <example>
/// Using Exceptions.Log with a custom exception type.
/// <code>
/// if (badFormat)
/// {
/// throw Exceptions.Log(new FormatException(Properties.Resources.MyClass_BadFormat));
/// }
/// </code>
/// </example>
public static T Log<T>(T ex, Type category)
where T : Exception => Log(ex, category, null);
/// <summary>
/// Logs the given exception as an error.
/// </summary>
/// <typeparam name="T">The type of exception to log and return.</typeparam>
/// <param name="ex">The exception to log.
/// <param name="contextProperties">Context information to log with the exception.</param>
/// <para/>Note: The <see cref="Exception.Data"/> dictionary
/// may get modified for this exception.</param>
/// <returns>The exception passed in as <paramref name="ex"/>.</returns>
/// <remarks>
/// See <see cref="Log<T>(T, Type)"/> for an example and more information.
/// </remarks>
public static T Log<T>(T ex, IDictionary<string, object>? contextProperties)
where T : Exception => Log(ex, null, contextProperties);
/// <summary>
/// Logs the given exception as an error.
/// </summary>
/// <typeparam name="T">The type of exception to log and return.</typeparam>
/// <param name="ex">The exception to log.
/// <param name="category">The configuration category, which is typically the caller's type.</param>
/// <param name="contextProperties">Context information to log with the exception.</param>
/// <para/>Note: The <see cref="Exception.Data"/> dictionary
/// may get modified for this exception.</param>
/// <returns>The exception passed in as <paramref name="ex"/>.</returns>
/// <remarks>
/// See <see cref="Log<T>(T, Type)"/> for an example and more information.
/// </remarks>
public static T Log<T>(T ex, Type? category, IDictionary<string, object>? contextProperties)
where T : Exception
{
// Make this a "quiet" precondition rather than throwing an exception
// because the caller is already trying to throw an exception.
if (category == null)
{
category = StackTraceUtility.GetSourceType(typeof(Exceptions));
}
Menees.Log.Error(category, null, ex, contextProperties);
return ex;
}
/// <summary>
/// Creates and logs a new InvalidOperationException with the specified message.
/// </summary>
/// <param name="message">The error message.</param>
/// <returns>A new exception instance.</returns>
public static InvalidOperationException NewInvalidOperationException(string message) => Log(new InvalidOperationException(message));
/// <summary>
/// Creates and logs a new InvalidOperationException with the specified message and context properties.
/// </summary>
/// <param name="message">The error message.</param>
/// <param name="contextProperties">Context information to log with the exception.</param>
/// <returns>A new exception instance.</returns>
public static InvalidOperationException NewInvalidOperationException(string message, IDictionary<string, object>? contextProperties)
=> Log(new InvalidOperationException(message), contextProperties);
/// <summary>
/// Enumerates all of the exceptions in an exception chain including handling multiple
/// inner exceptions for <see cref="AggregateException"/>.
/// </summary>
/// <param name="ex">The root exception to start from. <paramref name="action"/> will be called for this too.</param>
/// <param name="action">The action to invoke for the root exception and each inner exception. This is passed the
/// exception and its 0-based depth (where 0 is the root exception).</param>
public static void ForEach(Exception ex, Action<Exception, int> action)
{
Conditions.RequireReference(ex, nameof(ex));
Conditions.RequireReference(action, nameof(action));
ForEach(ex, 0, null, (exception, depth, outer) => action(exception, depth));
}
/// <summary>
/// Enumerates all of the exceptions in an exception chain including handling multiple
/// inner exceptions for <see cref="AggregateException"/>.
/// </summary>
/// <param name="ex">The root exception to start from. <paramref name="action"/> will be called for this too.</param>
/// <param name="action">The action to invoke for the root exception and each inner exception. This is passed the
/// exception, its 0-based depth (where 0 is the root exception), and the outer (i.e., parent) exception.</param>
public static void ForEach(Exception ex, Action<Exception, int, Exception?> action)
{
Conditions.RequireReference(ex, nameof(ex));
Conditions.RequireReference(action, nameof(action));
ForEach(ex, 0, null, action);
}
/// <summary>
/// Builds a message from all of the exceptions in an exception tree including handling
/// multiple inner exceptions for <see cref="AggregateException"/>s.
/// </summary>
/// <param name="ex">The root exception to start from.</param>
/// <returns>A string containing the tab-indented messages from each exception where
/// the indention level is based on the exception's depth in the tree.</returns>
public static string GetMessage(Exception ex)
{
StringBuilder sb = new();
ForEach(ex, (exception, depth, outer) => sb.Append('\t', depth).Append(exception.Message).AppendLine());
string result = sb.ToString().Trim();
return result;
}
/// <summary>
/// Gets whether an exception is an <see cref="IOException"/>, <see cref="SecurityException"/>,
/// or <see cref="UnauthorizedAccessException"/>.
/// </summary>
/// <param name="ex">The exception to check.</param>
public static bool IsAccessException(Exception ex)
{
bool result = ex != null && (ex is IOException || ex is UnauthorizedAccessException || ex is SecurityException);
return result;
}
#endregion
#region Private Methods
private static void ForEach(Exception ex, int depth, Exception? parent, Action<Exception, int, Exception?> action)
{
action(ex, depth, parent);
depth++;
if (ex is AggregateException aggregate)
{
foreach (Exception inner in aggregate.InnerExceptions)
{
ForEach(inner, depth, ex, action);
}
}
else
{
Exception? inner = ex.InnerException;
if (inner != null)
{
ForEach(inner, depth, ex, action);
}
}
}
#endregion
}
}