431 lines
16 KiB
C#
431 lines
16 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Diagnostics;
|
|||
|
|
using System.IO;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Runtime.CompilerServices;
|
|||
|
|
using System.Text;
|
|||
|
|
using System.Windows.Forms;
|
|||
|
|
using DTS.Common.Utilities.Properties;
|
|||
|
|
|
|||
|
|
// ReSharper disable once CheckNamespace
|
|||
|
|
namespace DTS.Common.Utilities.Logging
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// this class encapsulating writing to the log
|
|||
|
|
///
|
|||
|
|
/// </summary>
|
|||
|
|
public static class APILogger
|
|||
|
|
{
|
|||
|
|
private static readonly object ProcessLock = new object();
|
|||
|
|
private static readonly Dictionary<string, Stack<Stopwatch>> _processes = new Dictionary<string, Stack<Stopwatch>>();
|
|||
|
|
public static int LogLevel { get; set; } = 3;
|
|||
|
|
public static void StartProcess(string name)
|
|||
|
|
{
|
|||
|
|
lock (ProcessLock)
|
|||
|
|
{
|
|||
|
|
if (!_processes.ContainsKey(name)) { _processes[name] = new Stack<Stopwatch>(); }
|
|||
|
|
var sw = new Stopwatch();
|
|||
|
|
sw.Start();
|
|||
|
|
_processes[name].Push(sw);
|
|||
|
|
Log($"Starting process: [{name}] ({_processes[name].Count})");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static void StopProcess(string name)
|
|||
|
|
{
|
|||
|
|
lock (ProcessLock)
|
|||
|
|
{
|
|||
|
|
if (!_processes.ContainsKey(name) || 0 == _processes[name].Count)
|
|||
|
|
{
|
|||
|
|
Log($"Stopping process: [{name}] (0)");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
var sw = _processes[name].Pop();
|
|||
|
|
sw.Stop();
|
|||
|
|
var ts = new TimeSpan(sw.ElapsedTicks);
|
|||
|
|
Log($"Stopping process: [{name}], {TimespanToHumanReadable(ts)}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
private static string TimespanToHumanReadable(TimeSpan ts)
|
|||
|
|
{
|
|||
|
|
var sb = new StringBuilder();
|
|||
|
|
if (ts.TotalDays > 1)
|
|||
|
|
{
|
|||
|
|
sb.Append($"{System.Math.Floor(ts.TotalDays):0} days, ");
|
|||
|
|
}
|
|||
|
|
sb.Append($"{ts.Hours:00}:{ts.Minutes:00}:{ts.Seconds}.{ts.Milliseconds:000}");
|
|||
|
|
return sb.ToString();
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// describes an event that should be raised when there's an error that needs to be handled by an application
|
|||
|
|
/// </summary>
|
|||
|
|
public delegate void ErrorRaisedEventDelegate(string msg);
|
|||
|
|
|
|||
|
|
private static ErrorRaisedEventDelegate _OnErrorRaised;
|
|||
|
|
/// <summary>
|
|||
|
|
/// Error handler called when errors are raised
|
|||
|
|
/// </summary>
|
|||
|
|
public static ErrorRaisedEventDelegate OnErrorRaised
|
|||
|
|
{
|
|||
|
|
get => _OnErrorRaised;
|
|||
|
|
set => _OnErrorRaised = value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// raises an error, if an error handler is set, calls error handler
|
|||
|
|
/// </summary>
|
|||
|
|
public static void RaiseError(string error)
|
|||
|
|
{
|
|||
|
|
OnErrorRaised?.Invoke(error);
|
|||
|
|
}
|
|||
|
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
|||
|
|
public static string GetCurrentMethod()
|
|||
|
|
{
|
|||
|
|
var st = new StackTrace();
|
|||
|
|
var sf = st.GetFrame(1);
|
|||
|
|
|
|||
|
|
return sf.GetMethod().Name;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static bool _bDoPerformanceLog;
|
|||
|
|
public static void SetDoPerformanceLog(bool bDoLog)
|
|||
|
|
{
|
|||
|
|
_bDoPerformanceLog = bDoLog;
|
|||
|
|
}
|
|||
|
|
private static readonly object MyLock = new object();
|
|||
|
|
public static void DoPerformanceLog(string msg)
|
|||
|
|
{
|
|||
|
|
if (!_bDoPerformanceLog) { return; }
|
|||
|
|
var dt = DateTime.Now;
|
|||
|
|
var s = string.Format("{0:0000}-{1:00}-{2:00} {3:00}:{4:00}:{5:00}.{6:0000} ***{7}\r\n", dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, dt.Millisecond, msg);
|
|||
|
|
lock (MyLock)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
File.AppendAllText("PERFLOG.LOG", s);
|
|||
|
|
}
|
|||
|
|
catch (Exception)
|
|||
|
|
{
|
|||
|
|
// ignored - performance logging is only for statistics, we fail then we fail, just keep going
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// a function capable of logging a message
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="msg">message to log</param>
|
|||
|
|
public delegate void Sink(string msg);
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// a consumer of log messages
|
|||
|
|
/// </summary>
|
|||
|
|
public static Sink Writer;
|
|||
|
|
|
|||
|
|
public static Sink ConfigurationLogWriter;
|
|||
|
|
public static Sink ConfigReadErrorLogWriter;
|
|||
|
|
|
|||
|
|
public static Sink StateWriter;
|
|||
|
|
|
|||
|
|
private static string LogException(Exception ex, bool bPrepend, bool bNewLine)
|
|||
|
|
{
|
|||
|
|
if (Writer == null)
|
|||
|
|
{
|
|||
|
|
return "";
|
|||
|
|
}
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
if (ex == null)
|
|||
|
|
{
|
|||
|
|
Writer("LogException: called with null exception");
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
var sb = new StringBuilder();
|
|||
|
|
if (bPrepend) { sb.Append(string.Concat(DateTime.Now.ToString(Resources.APILogging_DateTime_Format), " ")); }
|
|||
|
|
ExceptionFormater(ref sb, ex, 0);
|
|||
|
|
sb = sb.Replace(Environment.NewLine, " ");
|
|||
|
|
if (bNewLine) { sb.AppendLine(); }
|
|||
|
|
return sb.ToString();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (Exception ownEx)
|
|||
|
|
{
|
|||
|
|
// and to be really paranoid...
|
|||
|
|
string orgMsg;
|
|||
|
|
if (ex == null)
|
|||
|
|
{
|
|||
|
|
orgMsg = "null exception";
|
|||
|
|
}
|
|||
|
|
else if (string.IsNullOrEmpty(ex.Message))
|
|||
|
|
{
|
|||
|
|
orgMsg = "blank exception message";
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
orgMsg = ex.Message;
|
|||
|
|
}
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
Writer("APILogger: LogException threw an expection: " + ownEx.Message + " when handling: " + orgMsg);
|
|||
|
|
}
|
|||
|
|
catch (Exception)
|
|||
|
|
{
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return "";
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// log an exception
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="ex">exception to be logged</param>
|
|||
|
|
public static void LogException(Exception ex)
|
|||
|
|
{
|
|||
|
|
Writer(LogException(ex, true, true));
|
|||
|
|
}
|
|||
|
|
public const string EXCEPTIONSTART_STRING = "!! ";
|
|||
|
|
private static void ExceptionFormater(ref StringBuilder sb, Exception ex, int level)
|
|||
|
|
{
|
|||
|
|
while (true)
|
|||
|
|
{
|
|||
|
|
if (ex == null)
|
|||
|
|
return;
|
|||
|
|
var front = new string(' ', level);
|
|||
|
|
sb.Append(EXCEPTIONSTART_STRING);
|
|||
|
|
sb.AppendFormat(Resources.APILogging_ExceptionFormatter_ExceptionOfTypeOccurredString, front, level > 0 ? Resources.APILogging_ExceptionFormatter_InnerIndicationString : Resources.APILogging_ExceptionFormatter_NonInnerIndicationString, ex.GetType());
|
|||
|
|
if (null != ex.TargetSite)
|
|||
|
|
{
|
|||
|
|
sb.AppendFormat(Resources.APILogging_ModuleNameDisplayString, front, NullGuard(ex.TargetSite.Module.Name), NullGuard(ex.TargetSite.Name));
|
|||
|
|
}
|
|||
|
|
sb.AppendFormat(Resources.APILogging_ExceptionMessageDisplayString, front, NullGuard(ex.Message));
|
|||
|
|
if (!string.IsNullOrEmpty(ex.StackTrace))
|
|||
|
|
{
|
|||
|
|
// RW: It appears .NET 4.5 has a subtle difference in stack formatting. I don't see it, but it's breaking the Regex.
|
|||
|
|
// I'm not sure why it's using regex in the first place. This should work for both, but has not been tested with .NET 2.0
|
|||
|
|
string[] delimeters = { Environment.NewLine };
|
|||
|
|
var lines = ex.StackTrace.Split(delimeters, 2, StringSplitOptions.None);
|
|||
|
|
if (lines.Length > 0)
|
|||
|
|
{
|
|||
|
|
sb.AppendFormat(Resources.APILogging_StackTraceDisplayString, front, NullGuard(lines[0]));
|
|||
|
|
if (lines.Length > 1)
|
|||
|
|
{
|
|||
|
|
sb.AppendFormat(Resources.APILogging_StackTraceDisplayString, front, NullGuard(lines[1]));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// now call ourself with inner
|
|||
|
|
ex = ex.InnerException;
|
|||
|
|
level = level + 1;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static string NullGuard(string s)
|
|||
|
|
{
|
|||
|
|
return s ?? Resources.Generic_NullIndicatorString;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static readonly string DATE_FORMAT = Resources.APILogging_DateTime_Format;
|
|||
|
|
private static string LogString(string str, bool bPrepend, bool bNewLine, bool bReplaceNewLines = true)
|
|||
|
|
{
|
|||
|
|
if (Writer == null)
|
|||
|
|
throw new Exception(Resources.APILogging_NullWriterDelegateString);
|
|||
|
|
var sb = new StringBuilder();
|
|||
|
|
if (bPrepend) { sb.Append($"{DateTime.Now.ToString(DATE_FORMAT)} "); }
|
|||
|
|
|
|||
|
|
if (bReplaceNewLines)
|
|||
|
|
{
|
|||
|
|
sb.Append(str.Replace(Environment.NewLine, " "));
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
sb.Append(str);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (bNewLine) { sb.AppendLine(); }
|
|||
|
|
return sb.ToString();
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// logs a string
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="str">string to be logged</param>
|
|||
|
|
public static void LogString(string str)
|
|||
|
|
{
|
|||
|
|
Writer?.Invoke(LogString(str, true, true));
|
|||
|
|
}
|
|||
|
|
private static readonly object PhaseShiftLogLock = new object();
|
|||
|
|
/// <summary>
|
|||
|
|
/// logs a phase shift into a table
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="serial"></param>
|
|||
|
|
/// <param name="msPhaseShift">MICROSECONDS of shift</param>
|
|||
|
|
/// <param name="originalT0"></param>
|
|||
|
|
/// <param name="modifiedTo"></param>
|
|||
|
|
/// <param name="samples"></param>
|
|||
|
|
/// <param name="rule"></param>
|
|||
|
|
public static void LogPhaseShift(string serial, double msPhaseShift, ulong originalT0, ulong modifiedTo, ulong samples, string rule)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var now = DateTime.Now;
|
|||
|
|
var s = string.Format("{0:0000}-{1:00}-{2:00} {3:00}:{4:00}:{5:00}.{6:0000}\t{7}\t{8}\t{9}\t{10}\t{11}\t{12}\r\n",
|
|||
|
|
now.Year,
|
|||
|
|
now.Month,
|
|||
|
|
now.Day,
|
|||
|
|
now.Hour,
|
|||
|
|
now.Minute,
|
|||
|
|
now.Second,
|
|||
|
|
now.Millisecond,
|
|||
|
|
serial,
|
|||
|
|
samples,
|
|||
|
|
msPhaseShift,
|
|||
|
|
originalT0,
|
|||
|
|
modifiedTo,
|
|||
|
|
rule
|
|||
|
|
);
|
|||
|
|
lock (PhaseShiftLogLock)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
File.WriteAllLines("PHASE_SHIFT_LOG.txt", new[] { s });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (Exception ex) { Log(ex); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static void StateLog(params object[] paramlist)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var sb = new StringBuilder();
|
|||
|
|
for (var i = 0; i < paramlist.Length; i++)
|
|||
|
|
{
|
|||
|
|
if (i > 0) { sb.Append(" "); }
|
|||
|
|
var s = paramlist[i] as string;
|
|||
|
|
if (paramlist[i] is DialogResult) { s = string.Format(" User selected {0}", ((DialogResult)paramlist[i])); }
|
|||
|
|
var ex = paramlist[i] as Exception;
|
|||
|
|
|
|||
|
|
if (null == s && null == ex && null != paramlist[i]) { s = paramlist[i].ToString(); }
|
|||
|
|
|
|||
|
|
var bPrepend = 0 == i;
|
|||
|
|
var bNewLine = paramlist.Length == i + 1;
|
|||
|
|
if (null != ex) { sb.Append(LogException(ex, bPrepend, bNewLine)); }
|
|||
|
|
else if (null != s) { sb.Append(LogString(s, bPrepend, bNewLine, false)); }
|
|||
|
|
}
|
|||
|
|
var msg = sb.ToString();
|
|||
|
|
StateWriter(msg);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
//if we fail to log there's not much more we can do ...
|
|||
|
|
Trace.WriteLine(ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
private const int LOG_LEVEL_DEBUG = 2;
|
|||
|
|
private static void LogEx(int logLevel, params object[] paramList)
|
|||
|
|
{
|
|||
|
|
if (logLevel >= LogLevel)
|
|||
|
|
{
|
|||
|
|
var newparams = new object[paramList.Length + 1];
|
|||
|
|
Array.Copy(new[] { $"LOG_LEVEL_{logLevel}" }, 0, newparams, 0, 1);
|
|||
|
|
Array.Copy(paramList, 0, newparams, 1, paramList.Length);
|
|||
|
|
Log(newparams);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
public static void DebugLog(params object[] paramList)
|
|||
|
|
{
|
|||
|
|
LogEx(LOG_LEVEL_DEBUG, paramList);
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// logs multiple parameters
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="paramlist">
|
|||
|
|
/// collection of parameters, can be exceptions, strings, or an object with tostring
|
|||
|
|
/// </param>
|
|||
|
|
public static void Log(params object[] paramlist)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var sb = new StringBuilder();
|
|||
|
|
for (var i = 0; i < paramlist.Length; i++)
|
|||
|
|
{
|
|||
|
|
var obj = paramlist[i];
|
|||
|
|
if (i > 0) { sb.Append(" "); }
|
|||
|
|
var s = obj as string;
|
|||
|
|
if (obj is DialogResult) { s = $" User selected {(DialogResult)obj}"; }
|
|||
|
|
else if (obj is byte[])
|
|||
|
|
{
|
|||
|
|
s = BitConverter.ToString((byte[])obj).Replace("-", "");
|
|||
|
|
}
|
|||
|
|
else if (obj is object[] objArray)
|
|||
|
|
{
|
|||
|
|
s = string.Join(", ", objArray);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var ex = obj as Exception;
|
|||
|
|
if (null == s && null == ex && null != obj) { s = obj.ToString(); }
|
|||
|
|
|
|||
|
|
var bPrepend = 0 == i;
|
|||
|
|
var bNewLine = paramlist.Length == i + 1;
|
|||
|
|
if (null != ex) { sb.Append(LogException(ex, bPrepend, bNewLine)); }
|
|||
|
|
else if (null != s) { sb.Append(LogString(s, bPrepend, bNewLine)); }
|
|||
|
|
}
|
|||
|
|
var msg = sb.ToString();
|
|||
|
|
Writer(msg);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
//if we fail to log there's not much more we can do ...
|
|||
|
|
Trace.WriteLine(ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// logs multiple parameters
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="paramlist">
|
|||
|
|
/// collection of parameters, can be exceptions, strings, or an object with tostring
|
|||
|
|
/// </param>
|
|||
|
|
public static void ConfLog(params object[] paramlist)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var sb = new StringBuilder();
|
|||
|
|
for (var i = 0; i < paramlist.Length; i++)
|
|||
|
|
{
|
|||
|
|
if (i > 0)
|
|||
|
|
{
|
|||
|
|
sb.Append(" ");
|
|||
|
|
}
|
|||
|
|
var s = paramlist[i] as string;
|
|||
|
|
if (paramlist[i] is DialogResult)
|
|||
|
|
{
|
|||
|
|
s = $" User selected {(DialogResult)paramlist[i]}";
|
|||
|
|
}
|
|||
|
|
var ex = paramlist[i] as Exception;
|
|||
|
|
|
|||
|
|
if (null == s && null == ex && null != paramlist[i])
|
|||
|
|
{
|
|||
|
|
s = paramlist[i].ToString();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var bPrepend = 0 == i;
|
|||
|
|
var bNewLine = paramlist.Length == i + 1;
|
|||
|
|
if (null != ex)
|
|||
|
|
{
|
|||
|
|
sb.Append(LogException(ex, bPrepend, bNewLine));
|
|||
|
|
}
|
|||
|
|
else if (null != s)
|
|||
|
|
{
|
|||
|
|
sb.Append(LogString(s, bPrepend, bNewLine));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
var msg = sb.ToString();
|
|||
|
|
ConfigurationLogWriter(msg);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
//as with the other log function, if we fail we don't have much backing we can do
|
|||
|
|
Trace.WriteLine(ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|