using System.Collections.Concurrent; using System.Diagnostics; 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 Logs { get; } protected ConcurrentBag 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 { if (_number <= 9) return "0" + _number; return _number.ToString(); } } public CallStackLog() { Logs = new ConcurrentDictionary(); ChildCallStackLogs = new ConcurrentBag(); _level = 0; _path = ""; _number = 1; } public CallStackLog(int level, string path) { Logs = new ConcurrentDictionary(); ChildCallStackLogs = new ConcurrentBag(); _level = level; _path = path; _number = 1; } public string LogMessage { get { StringBuilder stringBuilder = new StringBuilder(); foreach (var logLine in Logs.OrderBy(x => x.Key)) { stringBuilder.AppendLine(logLine.Key + $". Time - {logLine.Value} ms"); } return stringBuilder.ToString(); } } public ConcurrentDictionary 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; continue; } Logs.TryAdd(logLine.Key, logLine.Value); } } } ChildCallStackLogs = new ConcurrentBag(); } public void BeginCall(string message) { _currentCall = message; _stopwatch.Restart(); } public void EndCall() { _stopwatch.Stop(); lock (Logs) { Logs.TryAdd($"{_path}{this.Number} {_currentCall.ToStringOrEmpty()}", _stopwatch.ElapsedMilliseconds); _number++; } } } public class CallStackLogDB: CallStackLog, IDisposable { protected string _sessionKey; protected LogMessageType _type; protected string _target; protected string _action; protected DateTime _dateCreated; private bool disposed = false; public CallStackLogDB(string sessionKey, LogMessageType type, string target, string action):base() { _sessionKey = sessionKey; _type = type; _target = target; _action = action; _dateCreated = DateTime.UtcNow; } public void Dispose() { Dispose(true); // подавляем финализацию GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!disposed) { if (disposing) { this.Logs.TryAdd($"Full Time", Convert.ToInt64((DateTime.UtcNow - _dateCreated).TotalMilliseconds)); Kit.Helpers.Log.LoggerDB.Commit(this._sessionKey, this._type, this._target, this._action, _dateCreated, this.Logs.ToDictionary(x => x.Key, x => x.Value)); } // освобождаем неуправляемые объекты disposed = true; } } } }