using System; using System.Collections.Concurrent; using System.IO; using System.Threading; using System.Threading.Tasks; using Swan.Lite.Logging; using Swan.Threading; namespace Swan.Logging { /// /// A helper class to write into files the messages sent by the . /// /// public class FileLogger : TextLogger, ILogger { private readonly ManualResetEventSlim _doneEvent = new ManualResetEventSlim(true); private readonly ConcurrentQueue _logQueue = new ConcurrentQueue(); private readonly ExclusiveTimer _timer; private readonly string _filePath; private bool _disposedValue; // To detect redundant calls /// /// Initializes a new instance of the class. /// public FileLogger() : this(SwanRuntime.EntryAssemblyDirectory, true) { } /// /// Initializes a new instance of the class. /// /// The filePath. /// if set to true [daily file]. public FileLogger(string filePath, bool dailyFile) { _filePath = filePath; DailyFile = dailyFile; _timer = new ExclusiveTimer( async () => await WriteLogEntries().ConfigureAwait(false), TimeSpan.Zero, TimeSpan.FromSeconds(5)); } /// public LogLevel LogLevel { get; set; } /// /// Gets the file path. /// /// /// The file path. /// public string FilePath => DailyFile ? Path.Combine(Path.GetDirectoryName(_filePath), Path.GetFileNameWithoutExtension(_filePath) + $"_{DateTime.UtcNow:yyyyMMdd}" + Path.GetExtension(_filePath)) : _filePath; /// /// Gets a value indicating whether [daily file]. /// /// /// true if [daily file]; otherwise, false. /// public bool DailyFile { get; } /// public void Log(LogMessageReceivedEventArgs logEvent) { var (outputMessage, _) = GetOutputAndColor(logEvent); _logQueue.Enqueue(outputMessage); } /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_disposedValue) return; if (disposing) { _timer.Pause(); _timer.Dispose(); _doneEvent.Wait(); _doneEvent.Reset(); WriteLogEntries(true).Await(); _doneEvent.Dispose(); } _disposedValue = true; } private async Task WriteLogEntries(bool finalCall = false) { if (_logQueue.IsEmpty) return; if (!finalCall && !_doneEvent.IsSet) return; _doneEvent.Reset(); try { using (var file = File.AppendText(FilePath)) { while (!_logQueue.IsEmpty) { if (_logQueue.TryDequeue(out var entry)) await file.WriteAsync(entry).ConfigureAwait(false); } } } finally { if (!finalCall) _doneEvent.Set(); } } } }