This commit is contained in:
2024-04-27 09:08:28 +02:00
parent ad5f53462f
commit 1038a6fdee
3 changed files with 148 additions and 57 deletions

View File

@@ -1,10 +1,13 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using EonaCat.Logger.EonaCatCoreLogger.Internal;
using EonaCat.Logger.Managers;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -18,8 +21,6 @@ namespace EonaCat.Logger.EonaCatCoreLogger;
[ProviderAlias("EonaCatFileLogger")]
public class FileLoggerProvider : BatchingLoggerProvider
{
private static readonly object WriteLock = new();
private static readonly object RollOverLock = new();
private readonly string _fileNamePrefix;
private readonly int _maxFileSize;
private readonly int _maxRetainedFiles;
@@ -29,6 +30,9 @@ public class FileLoggerProvider : BatchingLoggerProvider
private string _logFile;
private bool _rollingOver;
private int _rollOverCount;
private ConcurrentDictionary<string,string> _buffer = new ConcurrentDictionary<string, string>();
public event EventHandler<ErrorMessage> OnError;
/// <summary>
/// Creates an instance of the <see cref="FileLoggerProvider" />
@@ -70,60 +74,97 @@ public class FileLoggerProvider : BatchingLoggerProvider
CancellationToken cancellationToken)
{
Directory.CreateDirectory(_path);
foreach (var group in messages.GroupBy(GetGrouping))
{
LogFile = GetFullName(group.Key);
var fileInfo = new FileInfo(LogFile);
if (_maxFileSize > 0 && fileInfo.Exists && fileInfo.Length > _maxFileSize)
if (_maxRolloverFiles > 0 && _rollOverCount >= 0)
{
if (_rollOverCount < _maxRolloverFiles)
{
var rollOverFile = LogFile.Replace(".log", $"_{++_rollOverCount}.log");
if (File.Exists(rollOverFile)) File.Delete(rollOverFile);
fileInfo.CopyTo(rollOverFile);
File.WriteAllText(LogFile, string.Empty);
}
else
{
lock (RollOverLock)
{
_rollingOver = true;
MoveRolloverLogFiles();
_rollingOver = false;
}
}
}
while (_rollingOver) await Task.Delay(100, cancellationToken).ConfigureAwait(false);
lock (WriteLock)
var currentMessages = string.Join(string.Empty, group.Select(item => item.Message));
if (!_buffer.TryAdd(LogFile, currentMessages))
{
var tries = 0;
var completed = false;
_buffer[LogFile] += currentMessages;
}
while (!completed)
try
{
var file = new StreamWriter(LogFile, true);
foreach (var item in group) file.Write(item.Message);
file.Close();
completed = true;
}
catch (Exception exc)
{
tries++;
Task.Delay(100);
if (tries >= _maxTries)
throw;
}
await MoveRolloverLogFilesAsync(fileInfo, cancellationToken).ConfigureAwait(false);
if (await TryWriteToFileAsync(cancellationToken))
{
// Clear buffer on success
_buffer.Clear();
}
else if (await WriteToTempFileAsync(cancellationToken))
{
// Fallback to temp file
}
DeleteOldLogFiles();
}
}
private async Task<bool> TryWriteToFileAsync(CancellationToken cancellationToken)
{
if (!_buffer.ContainsKey(LogFile)) return true;
var tries = 0;
var completed = false;
while (!completed)
{
try
{
using (var file = new StreamWriter(LogFile, true))
{
await file.WriteAsync(_buffer[LogFile]).ConfigureAwait(false);
}
completed = true;
_buffer.TryRemove(LogFile, out _);
return true; // Success
}
catch (Exception)
{
tries++;
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
if (tries >= _maxTries)
{
OnError?.Invoke(this, new ErrorMessage { Message = "Cannot write to log folder"});
return false; // Failure after retries
}
}
}
return false;
}
private async Task<bool> WriteToTempFileAsync(CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(LogFile) || !_buffer.ContainsKey(LogFile))
{
return false;
}
var tempLogFolder = Path.Combine(Path.GetTempPath(), "EonaCatLogs");
var tempLogFile = $"{Path.Combine(tempLogFolder, Path.GetFileNameWithoutExtension(LogFile))}.log";
try
{
Directory.CreateDirectory(tempLogFolder);
// Create new temp file
using (var file = new StreamWriter(tempLogFile, true))
{
await file.WriteAsync(_buffer[LogFile]).ConfigureAwait(false);
}
_buffer.TryRemove(LogFile, out _);
return true;
}
catch (Exception exception)
{
OnError?.Invoke(this, new ErrorMessage { Message = "Cannot write to temp folder ", Exception = exception});
return false;
}
}
private string GetFullName((int Year, int Month, int Day) group)
{
var hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix);
@@ -148,31 +189,78 @@ public class FileLoggerProvider : BatchingLoggerProvider
destination.LastAccessTime = origin.LastAccessTime;
}
private async Task MoveRolloverLogFilesAsync(FileInfo fileInfo, CancellationToken cancellationToken)
{
try
{
if (_maxFileSize > 0 && fileInfo.Exists && fileInfo.Length > _maxFileSize)
{
if (_maxRolloverFiles > 0 && _rollOverCount >= 0)
{
if (_rollOverCount < _maxRolloverFiles)
{
var rollOverFile = LogFile.Replace(".log", $"_{++_rollOverCount}.log");
if (File.Exists(rollOverFile))
{
File.Delete(rollOverFile);
}
fileInfo.CopyTo(rollOverFile);
File.WriteAllText(LogFile, string.Empty);
}
else
{
lock (_rollOverLock)
{
_rollingOver = true;
MoveRolloverLogFiles();
_rollingOver = false;
}
}
}
}
while (_rollingOver)
{
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
}
}
catch (Exception exception)
{
OnError?.Invoke(this, new ErrorMessage { Message = "Cannot rollover files" });
}
}
private object _rollOverLock { get; set; } = new object();
/// <summary>
/// Rollover logFiles
/// Rollover logFiles
/// </summary>
protected void MoveRolloverLogFiles()
private void MoveRolloverLogFiles()
{
if (_maxRolloverFiles > 0 && _rollOverCount >= 0)
{
if (_rollOverCount >= _maxRolloverFiles)
{
var maxRollover = _rollOverCount;
var hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix);
bool hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix);
IEnumerable<FileInfo> files;
if (hasPrefix)
{
files = new DirectoryInfo(_path).GetFiles(_fileNamePrefix + "*").OrderBy(x => x.CreationTime);
}
else
{
files = new DirectoryInfo(_path).GetFiles("*").OrderBy(x => x.CreationTime);
}
for (var i = files.Count() - 1; i >= 0; i--)
for (int i = files.Count() - 1; i >= 0; i--)
{
var currentFile = files.ElementAt(i);
if (i == 0)
{
// Temporary move first file
var newFilename2 = Path.GetFileName(currentFile.FullName).Replace(".log", $"_{i + 1}.log");
MoveFile(currentFile.FullName,
$@"{Path.GetDirectoryName(currentFile.FullName)}\{newFilename2}");
var newFilename2 = Path.GetFileName(currentFile.FullName).Replace($".log", $"_{i + 1}.log");
MoveFile(currentFile.FullName, $@"{Path.GetDirectoryName(currentFile.FullName)}\{newFilename2}");
continue;
}
@@ -186,9 +274,9 @@ public class FileLoggerProvider : BatchingLoggerProvider
var newFilename = Path.GetFileName(currentFile.FullName).Replace($"_{i}.log", $"_{i + 1}.log");
MoveFile(currentFile.FullName, $@"{Path.GetDirectoryName(currentFile.FullName)}\{newFilename}");
}
_rollOverCount = 0;
}
}
}
/// <summary>