Lean  $LEAN_TAG$
Log.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.Collections;
18 using System.Globalization;
19 using System.Runtime.CompilerServices;
20 using System.Text;
21 using System.Text.RegularExpressions;
22 
23 namespace QuantConnect.Logging
24 {
25  /// <summary>
26  /// Logging management class.
27  /// </summary>
28  public static class Log
29  {
30  private static readonly Regex LeanPathRegex = new Regex("(?:\\S*?\\\\pythonnet\\\\)|(?:\\S*?\\\\Lean\\\\)|(?:\\S*?/Lean/)|(?:\\S*?/pythonnet/)", RegexOptions.Compiled);
31  private static string _lastTraceText = "";
32  private static string _lastErrorText = "";
33  private static bool _debuggingEnabled;
34  private static int _level = 1;
35  private static ILogHandler _logHandler = new ConsoleLogHandler();
36 
37  /// <summary>
38  /// Gets or sets the ILogHandler instance used as the global logging implementation.
39  /// </summary>
40  public static ILogHandler LogHandler
41  {
42  get { return _logHandler; }
43  set { _logHandler = value; }
44  }
45 
46  /// <summary>
47  /// Global flag whether to enable debugging logging:
48  /// </summary>
49  public static bool DebuggingEnabled
50  {
51  get { return _debuggingEnabled; }
52  set { _debuggingEnabled = value; }
53  }
54 
55  /// <summary>
56  /// Global flag to specify file based log path
57  /// </summary>
58  /// <remarks>Only valid for file based loggers</remarks>
59  public static string FilePath { get; set; } = "log.txt";
60 
61  /// <summary>
62  /// Set the minimum message level:
63  /// </summary>
64  public static int DebuggingLevel
65  {
66  get { return _level; }
67  set { _level = value; }
68  }
69 
70  /// <summary>
71  /// Log error
72  /// </summary>
73  /// <param name="error">String Error</param>
74  /// <param name="overrideMessageFloodProtection">Force sending a message, overriding the "do not flood" directive</param>
75  public static void Error(string error, bool overrideMessageFloodProtection = false)
76  {
77  try
78  {
79  if (error == _lastErrorText && !overrideMessageFloodProtection) return;
80  _logHandler.Error(error);
81  _lastErrorText = error; //Stop message flooding filling diskspace.
82  }
83  catch (Exception err)
84  {
85  Console.WriteLine("Log.Error(): Error writing error: " + err.Message);
86  }
87  }
88 
89  /// <summary>
90  /// Log error. This overload is usefull when exceptions are being thrown from within an anonymous function.
91  /// </summary>
92  /// <param name="method">The method identifier to be used</param>
93  /// <param name="exception">The exception to be logged</param>
94  /// <param name="message">An optional message to be logged, if null/whitespace the messge text will be extracted</param>
95  /// <param name="overrideMessageFloodProtection">Force sending a message, overriding the "do not flood" directive</param>
96  private static void Error(string method, Exception exception, string message = null, bool overrideMessageFloodProtection = false)
97  {
98  message = method + "(): " + (message ?? string.Empty) + " " + ClearLeanPaths(exception?.ToString());
99  Error(message, overrideMessageFloodProtection);
100  }
101 
102  /// <summary>
103  /// Log error
104  /// </summary>
105  /// <param name="exception">The exception to be logged</param>
106  /// <param name="message">An optional message to be logged, if null/whitespace the messge text will be extracted</param>
107  /// <param name="overrideMessageFloodProtection">Force sending a message, overriding the "do not flood" directive</param>
108  [MethodImpl(MethodImplOptions.NoInlining)]
109  public static void Error(Exception exception, string message = null, bool overrideMessageFloodProtection = false)
110  {
111  Error(WhoCalledMe.GetMethodName(1), exception, message, overrideMessageFloodProtection);
112  }
113 
114  /// <summary>
115  /// Log trace
116  /// </summary>
117  public static void Trace(string traceText, bool overrideMessageFloodProtection = false)
118  {
119  try
120  {
121  if (traceText == _lastTraceText && !overrideMessageFloodProtection) return;
122  _logHandler.Trace(traceText);
123  _lastTraceText = traceText;
124  }
125  catch (Exception err)
126  {
127  Console.WriteLine("Log.Trace(): Error writing trace: " +err.Message);
128  }
129  }
130 
131  /// <summary>
132  /// Writes the message in normal text
133  /// </summary>
134  public static void Trace(string format, params object[] args)
135  {
136  Trace(string.Format(CultureInfo.InvariantCulture, format, args));
137  }
138 
139  /// <summary>
140  /// Writes the message in red
141  /// </summary>
142  public static void Error(string format, params object[] args)
143  {
144  Error(string.Format(CultureInfo.InvariantCulture, format, args));
145  }
146 
147  /// <summary>
148  /// Output to the console
149  /// </summary>
150  /// <param name="text">The message to show</param>
151  /// <param name="level">debug level</param>
152  public static void Debug(string text, int level = 1)
153  {
154  try
155  {
156  if (!_debuggingEnabled || level < _level) return;
157  _logHandler.Debug(text);
158  }
159  catch (Exception err)
160  {
161  Console.WriteLine("Log.Debug(): Error writing debug: " + err.Message);
162  }
163  }
164 
165  /// <summary>
166  /// C# Equivalent of Print_r in PHP:
167  /// </summary>
168  /// <param name="obj"></param>
169  /// <param name="recursion"></param>
170  /// <returns></returns>
171  public static string VarDump(object obj, int recursion = 0)
172  {
173  var result = new StringBuilder();
174 
175  // Protect the method against endless recursion
176  if (recursion < 5)
177  {
178  // Determine object type
179  var t = obj.GetType();
180 
181  // Get array with properties for this object
182  var properties = t.GetProperties();
183 
184  foreach (var property in properties)
185  {
186  try
187  {
188  // Get the property value
189  var value = property.GetValue(obj, null);
190 
191  // Create indenting string to put in front of properties of a deeper level
192  // We'll need this when we display the property name and value
193  var indent = String.Empty;
194  var spaces = "| ";
195  var trail = "|...";
196 
197  if (recursion > 0)
198  {
199  indent = new StringBuilder(trail).Insert(0, spaces, recursion - 1).ToString();
200  }
201 
202  if (value != null)
203  {
204  // If the value is a string, add quotation marks
205  var displayValue = value.ToString();
206  if (value is string) displayValue = String.Concat('"', displayValue, '"');
207 
208  // Add property name and value to return string
209  result.AppendFormat(CultureInfo.InvariantCulture, "{0}{1} = {2}\n", indent, property.Name, displayValue);
210 
211  try
212  {
213  if (!(value is ICollection))
214  {
215  // Call var_dump() again to list child properties
216  // This throws an exception if the current property value
217  // is of an unsupported type (eg. it has not properties)
218  result.Append(VarDump(value, recursion + 1));
219  }
220  else
221  {
222  // 2009-07-29: added support for collections
223  // The value is a collection (eg. it's an arraylist or generic list)
224  // so loop through its elements and dump their properties
225  var elementCount = 0;
226  foreach (var element in ((ICollection)value))
227  {
228  var elementName = $"{property.Name}[{elementCount}]";
229  indent = new StringBuilder(trail).Insert(0, spaces, recursion).ToString();
230 
231  // Display the collection element name and type
232  result.AppendFormat(CultureInfo.InvariantCulture, "{0}{1} = {2}\n", indent, elementName, element.ToString());
233 
234  // Display the child properties
235  result.Append(VarDump(element, recursion + 2));
236  elementCount++;
237  }
238 
239  result.Append(VarDump(value, recursion + 1));
240  }
241  } catch { }
242  }
243  else
244  {
245  // Add empty (null) property to return string
246  result.AppendFormat(CultureInfo.InvariantCulture, "{0}{1} = {2}\n", indent, property.Name, "null");
247  }
248  }
249  catch
250  {
251  // Some properties will throw an exception on property.GetValue()
252  // I don't know exactly why this happens, so for now i will ignore them...
253  }
254  }
255  }
256 
257  return result.ToString();
258  }
259 
260  /// <summary>
261  /// Helper method to clear undesired paths from stack traces
262  /// </summary>
263  /// <param name="error">The error to cleanup</param>
264  /// <returns>The sanitized error</returns>
265  public static string ClearLeanPaths(string error)
266  {
267  if (string.IsNullOrEmpty(error))
268  {
269  return error;
270  }
271  return LeanPathRegex.Replace(error, string.Empty);
272  }
273  }
274 }