RaspberryIO_26/Swan.Lite/Logging/FileLogger.cs
2019-12-08 19:54:52 +01:00

124 lines
3.7 KiB
C#

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