using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Valve.VR.InteractionSystem; namespace Logging.Base { public class FileLogger { private readonly List logables; private readonly DateTime startTimestamp; private readonly Dictionary> waitingBuffers; private AsyncLogFileWriter writer; private bool writerAvailable; private FileLogger() { waitingBuffers = new Dictionary>(); startTimestamp = DateTime.Now; logables = new List(); } public string SubFolderName { get; set; } public string Condition { get; set; } public void RegisterLogable(ILogable l) { logables.Add(l); } private IEnumerable MergedHeaders() { var headers = new List {"timestamp"}; foreach (var l in logables) headers.AddRange(l.HeaderNames); return headers.ToArray(); } private IEnumerable MergeTimestamps() { var allTimestamps = new HashSet(); long minBegin = -1; long minEnd = -1; foreach (var l in logables) { var keys = l.BufferLines.Keys.ToArray(); var begin = keys.Length > 0 ? keys.First() : -1; var end = keys.Length > 0 ? keys.Last() : -1; if (minBegin == -1 || begin >= 0 && begin < minBegin) minBegin = begin; if (minEnd == -1 || end >= 0 && end < minEnd) minEnd = end; } foreach (var l in logables) { var bufferLines = l.BufferLines; var timestamps = new HashSet(bufferLines.Keys); var logableKey = l.Key; var hasWaiting = waitingBuffers.TryGetValue(logableKey, out var waiting); if (hasWaiting) foreach (var waitingTs in waiting.Keys) if (waitingTs <= minEnd) allTimestamps.Add(waitingTs); foreach (var t in timestamps) if (t >= minBegin && t <= minEnd) { allTimestamps.Add(t); } else { if (waitingBuffers.ContainsKey(l.Key)) waitingBuffers[l.Key][t] = bufferLines[t]; else waitingBuffers[l.Key] = new Dictionary {{t, bufferLines[t]}}; } } logables.ForEach(l => allTimestamps.UnionWith(l.BufferLines.Keys.Where(k => k >= minBegin && k <= minEnd))); return allTimestamps; } private IEnumerable> MergedLines() { var lines = new List>(); var allKeys = MergeTimestamps(); var mergedDict = new SortedDictionary>(); foreach (var timestamp in allKeys) { var newItem = new List(); foreach (var l in logables) { var logableKey = l.Key; var hasWaiting = waitingBuffers.TryGetValue(logableKey, out var waiting); string[] linePart; if (l.BufferLines.ContainsKey(timestamp)) { linePart = l.BufferLines[timestamp]; } else if (hasWaiting && waiting.ContainsKey(timestamp)) { linePart = waiting[timestamp]; waiting.Remove(timestamp); } else { /*var lastAvailableKey = l.BufferLines.Keys.Where(k => k < timestamp).DefaultIfEmpty(-1).LastOrDefault(); linePart = lastAvailableKey >= 0 ? l.BufferLines[lastAvailableKey] : new string[l.HeaderNames.ToArray().Length];*/ linePart = new string[l.HeaderNames.ToArray().Length]; } newItem.AddRange(linePart); } mergedDict[timestamp] = newItem; } mergedDict.ForEach(item => { var listItem = new List {item.Key.ToString("D")}; listItem.AddRange(item.Value); lines.Add(listItem); }); return lines; } public async Task UpdateRegisteredLogs() { if (!writerAvailable) { var subfolder = SubFolderName != null ? $"/{SubFolderName}/" : ""; var condition = $"_{Condition}" ?? ""; writer = new AsyncLogFileWriter( $"Assets/Logs/{subfolder}{startTimestamp:yyyy-MM-dd_HHmmss}{condition}.tsv"); await writer.WriteDataLine(MergedHeaders()); writerAvailable = true; } await writer.WriteDataLines(MergedLines()); logables.ForEach(l => l.ClearBuffer()); } private void Dispose() { if (writerAvailable) writer.Dispose(); logables.Clear(); } #region signleton private static Lazy instance = new Lazy (() => new FileLogger()); public static FileLogger Instance => instance.Value; public static void DestroyInstance() { if (instance.IsValueCreated) { instance.Value.Dispose(); instance = new Lazy (() => new FileLogger()); } } #endregion } }