2019-12-04 18:57:18 +01:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using Swan.Lite.Logging;
|
|
|
|
|
using Swan.Threading;
|
|
|
|
|
|
2019-12-08 19:54:52 +01:00
|
|
|
|
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
|
|
|
|
|
|
2019-12-04 18:57:18 +01:00
|
|
|
|
/// <summary>
|
2019-12-08 19:54:52 +01:00
|
|
|
|
/// Initializes a new instance of the <see cref="FileLogger"/> class.
|
2019-12-04 18:57:18 +01:00
|
|
|
|
/// </summary>
|
2019-12-08 19:54:52 +01:00
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-12-04 18:57:18 +01:00
|
|
|
|
}
|