223 lines
6.4 KiB
C#
223 lines
6.4 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
namespace Kit.Helpers.Log
|
|
{
|
|
public interface ICallStackLog
|
|
{
|
|
string LogMessage { get; }
|
|
ICallStackLog NextLevel();
|
|
void FinishLevel();
|
|
void BeginCall(string message);
|
|
void EndCall();
|
|
}
|
|
|
|
public class EmptyCallStackLog : ICallStackLog
|
|
{
|
|
public string LogMessage
|
|
{
|
|
get { return ""; }
|
|
}
|
|
|
|
public ICallStackLog NextLevel()
|
|
{
|
|
return CallStackLog.Empty;
|
|
}
|
|
|
|
public void FinishLevel()
|
|
{
|
|
return;
|
|
}
|
|
|
|
public void BeginCall(string message)
|
|
{
|
|
return;
|
|
}
|
|
|
|
public void EndCall()
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
public class CallStackLog : ICallStackLog
|
|
{
|
|
protected ConcurrentDictionary<string, long> Logs { get; }
|
|
protected ConcurrentBag<CallStackLog> ChildCallStackLogs { get; set; }
|
|
protected readonly int _level;
|
|
protected readonly string _path;
|
|
protected string _currentCall;
|
|
protected int _number;
|
|
protected readonly Stopwatch _stopwatch = new Stopwatch();
|
|
public static ICallStackLog Empty { get { return new EmptyCallStackLog(); } }
|
|
|
|
protected string Number
|
|
{
|
|
get
|
|
{
|
|
return _number.ToString("D5");
|
|
}
|
|
}
|
|
public CallStackLog()
|
|
{
|
|
Logs = new ConcurrentDictionary<string, long>();
|
|
ChildCallStackLogs = new ConcurrentBag<CallStackLog>();
|
|
_level = 0;
|
|
_path = "";
|
|
_number = 1;
|
|
}
|
|
|
|
public CallStackLog(int level, string path)
|
|
{
|
|
Logs = new ConcurrentDictionary<string, long>();
|
|
ChildCallStackLogs = new ConcurrentBag<CallStackLog>();
|
|
_level = level;
|
|
_path = path;
|
|
_number = 1;
|
|
}
|
|
|
|
public string LogMessage
|
|
{
|
|
get
|
|
{
|
|
StringBuilder stringBuilder = new StringBuilder();
|
|
foreach (var logLine in Logs.OrderBy(x => x.Key))
|
|
{
|
|
string time = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000}", logLine.Value / 1000.0f);
|
|
|
|
stringBuilder.AppendLine(logLine.Key + $". Time - {time} ms");
|
|
}
|
|
return stringBuilder.ToString();
|
|
}
|
|
}
|
|
public ConcurrentDictionary<string, long> GetLogs
|
|
{
|
|
get {
|
|
return this.Logs;
|
|
}
|
|
}
|
|
|
|
public ICallStackLog NextLevel()
|
|
{
|
|
var childCallStackLog = new CallStackLog(_level + 1, _path + this.Number + ".");
|
|
ChildCallStackLogs.Add(childCallStackLog);
|
|
return childCallStackLog;
|
|
}
|
|
|
|
public void FinishLevel()
|
|
{
|
|
foreach (CallStackLog childCallStackLog in ChildCallStackLogs)
|
|
{
|
|
lock (Logs)
|
|
{
|
|
foreach (var logLine in childCallStackLog.Logs)
|
|
{
|
|
if (Logs.ContainsKey(logLine.Key))
|
|
{
|
|
Logs[logLine.Key] += logLine.Value;
|
|
}
|
|
else
|
|
{
|
|
Logs[logLine.Key] = logLine.Value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ChildCallStackLogs = new ConcurrentBag<CallStackLog>();
|
|
}
|
|
|
|
public void BeginCall(string message)
|
|
{
|
|
_currentCall = message;
|
|
_stopwatch.Restart();
|
|
}
|
|
|
|
public void EndCall()
|
|
{
|
|
_stopwatch.Stop();
|
|
lock (Logs)
|
|
{
|
|
long ticksByMicrosecond = Stopwatch.Frequency / (1000L * 1000L);
|
|
long microSeconds = _stopwatch.ElapsedTicks / ticksByMicrosecond;
|
|
Logs.TryAdd($"{_path}{this.Number} {_currentCall.ToStringOrEmpty()}", microSeconds);
|
|
_number++;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class CallStackLogFile : ICallStackLog
|
|
{
|
|
protected object _lock = new object();
|
|
protected ConcurrentBag<CallStackLogFile> ChildCallStackLogs { get; set; }
|
|
protected readonly int _level;
|
|
protected readonly string _path;
|
|
protected string _currentCall;
|
|
protected int _number;
|
|
protected readonly Stopwatch _stopwatch = new Stopwatch();
|
|
protected readonly StreamWriter _writer;
|
|
public static ICallStackLog Empty { get { return new EmptyCallStackLog(); } }
|
|
|
|
protected string Number
|
|
{
|
|
get
|
|
{
|
|
return _number.ToString("D5");
|
|
}
|
|
}
|
|
public CallStackLogFile(StreamWriter writer)
|
|
{
|
|
_writer = writer;
|
|
ChildCallStackLogs = new ConcurrentBag<CallStackLogFile>();
|
|
_level = 0;
|
|
_path = "";
|
|
_number = 1;
|
|
}
|
|
|
|
public CallStackLogFile(StreamWriter writer, int level, string path)
|
|
{
|
|
_writer = writer;
|
|
ChildCallStackLogs = new ConcurrentBag<CallStackLogFile>();
|
|
_level = level;
|
|
_path = path;
|
|
_number = 1;
|
|
}
|
|
|
|
public string LogMessage => string.Empty;
|
|
|
|
public ICallStackLog NextLevel()
|
|
{
|
|
var childCallStackLog = new CallStackLogFile(_writer, _level + 1, _path + this.Number + ".");
|
|
ChildCallStackLogs.Add(childCallStackLog);
|
|
return childCallStackLog;
|
|
}
|
|
|
|
public void FinishLevel()
|
|
{
|
|
ChildCallStackLogs = new ConcurrentBag<CallStackLogFile>();
|
|
}
|
|
|
|
public void BeginCall(string message)
|
|
{
|
|
_currentCall = message;
|
|
_stopwatch.Restart();
|
|
}
|
|
|
|
public void EndCall()
|
|
{
|
|
_stopwatch.Stop();
|
|
lock (_lock)
|
|
{
|
|
long microSeconds = _stopwatch.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
|
|
|
|
string time = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000}", microSeconds / 1000.0f);
|
|
|
|
_writer?.WriteLine($"{_path}{this.Number} {_currentCall.ToStringOrEmpty()}. Time - {time} ms");
|
|
_number++;
|
|
}
|
|
}
|
|
}
|
|
}
|