Lean  $LEAN_TAG$
StackExceptionInterpreter.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using System;
17 using System.Linq;
18 using System.Reflection;
19 using QuantConnect.Logging;
20 using System.Collections.Generic;
21 
23 {
24  /// <summary>
25  /// Interprets exceptions using the configured interpretations
26  /// </summary>
28  {
29  private readonly List<IExceptionInterpreter> _interpreters;
30 
31  /// <summary>
32  /// Stack interpreter instance
33  /// </summary>
34  public static readonly Lazy<StackExceptionInterpreter> Instance = new Lazy<StackExceptionInterpreter>(
35  () => StackExceptionInterpreter.CreateFromAssemblies(AppDomain.CurrentDomain.GetAssemblies()));
36 
37  /// <summary>
38  /// Determines the order that an instance of this class should be called
39  /// </summary>
40  public int Order => 0;
41 
42  /// <summary>
43  /// Initializes a new instance of the <see cref="StackExceptionInterpreter"/> class
44  /// </summary>
45  /// <param name="interpreters">The interpreters to use</param>
46  public StackExceptionInterpreter(IEnumerable<IExceptionInterpreter> interpreters)
47  {
48  _interpreters = interpreters.OrderBy(x => x.Order).ToList();
49  }
50 
51  /// <summary>
52  /// Gets the interpreters loaded into this instance
53  /// </summary>
54  public IEnumerable<IExceptionInterpreter> Interpreters => _interpreters;
55 
56  /// <summary>
57  /// Determines if this interpreter should be applied to the specified exception.
58  /// </summary>
59  /// <param name="exception">The exception to check</param>
60  /// <returns>True if the exception can be interpreted, false otherwise</returns>
61  public bool CanInterpret(Exception exception)
62  {
63  return _interpreters.Any(interpreter => interpreter.CanInterpret(exception));
64  }
65 
66  /// <summary>
67  /// Interprets the specified exception into a new exception
68  /// </summary>
69  /// <param name="exception">The exception to be interpreted</param>
70  /// <param name="innerInterpreter">An interpreter that should be applied to the inner exception.
71  /// This provides a link back allowing the inner exceptions to be interpreted using the intepretators
72  /// configured in the <see cref="StackExceptionInterpreter"/>. Individual implementations *may* ignore
73  /// this value if required.</param>
74  /// <returns>The interpreted exception</returns>
75  public Exception Interpret(Exception exception, IExceptionInterpreter innerInterpreter = null)
76  {
77  if (exception == null)
78  {
79  return null;
80  }
81 
82  foreach (var interpreter in _interpreters)
83  {
84  try
85  {
86  if (interpreter.CanInterpret(exception))
87  {
88  // use this instance to interpret inner exceptions as well, unless one was specified
89  return interpreter.Interpret(exception, innerInterpreter ?? this);
90  }
91  }
92  catch (Exception err)
93  {
94  Log.Error(err);
95  }
96  }
97 
98  return exception;
99  }
100 
101  /// <summary>
102  /// Combines the exception messages from this exception and all inner exceptions.
103  /// </summary>
104  /// <param name="exception">The exception to create a collated message from</param>
105  /// <returns>The collate exception message</returns>
106  public string GetExceptionMessageHeader(Exception exception)
107  {
108  return string.Join(" ", InnersAndSelf(exception).Select(e => e.Message));
109  }
110 
111  /// <summary>
112  /// Creates a new <see cref="StackExceptionInterpreter"/> by loading implementations with default constructors from the specified assemblies
113  /// </summary>
114  /// <param name="assemblies">The assemblies to scan</param>
115  /// <returns>A new <see cref="StackExceptionInterpreter"/> containing interpreters from the specified assemblies</returns>
116  public static StackExceptionInterpreter CreateFromAssemblies(IEnumerable<Assembly> assemblies)
117  {
118  var interpreters =
119  from assembly in assemblies
120  from type in assembly.GetTypes()
121  // ignore non-public and non-instantiable abstract types
122  where type.IsPublic && !type.IsAbstract
123  // type implements IExceptionInterpreter
124  where typeof(IExceptionInterpreter).IsAssignableFrom(type)
125  // type is not mocked with MOQ library
126  where type.FullName != null && !type.FullName.StartsWith("Castle.Proxies.ObjectProxy")
127  // type has default parameterless ctor
128  where type.GetConstructor(new Type[0]) != null
129  // provide guarantee of deterministic ordering
130  orderby type.FullName
131  select (IExceptionInterpreter) Activator.CreateInstance(type);
132 
133  var stackExceptionInterpreter = new StackExceptionInterpreter(interpreters);
134 
135  foreach (var interpreter in stackExceptionInterpreter.Interpreters)
136  {
137  Log.Debug(Messages.StackExceptionInterpreter.LoadedExceptionInterpreter(interpreter));
138  }
139 
140  return stackExceptionInterpreter;
141  }
142 
143  private IEnumerable<Exception> InnersAndSelf(Exception exception)
144  {
145  yield return exception;
146  while (exception.InnerException != null)
147  {
148  exception = exception.InnerException;
149  yield return exception;
150  }
151  }
152  }
153 }