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

@@ -3,7 +3,7 @@
<TargetFrameworks>.netstandard2.1; net6.0; net7.0; net8.0; net4.8;</TargetFrameworks> <TargetFrameworks>.netstandard2.1; net6.0; net7.0; net8.0; net4.8;</TargetFrameworks>
<ApplicationIcon>icon.ico</ApplicationIcon> <ApplicationIcon>icon.ico</ApplicationIcon>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<FileVersion>1.2.6</FileVersion> <FileVersion>1.2.7</FileVersion>
<Authors>EonaCat (Jeroen Saey)</Authors> <Authors>EonaCat (Jeroen Saey)</Authors>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Company>EonaCat (Jeroen Saey)</Company> <Company>EonaCat (Jeroen Saey)</Company>
@@ -24,7 +24,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<EVRevisionFormat>1.2.6+{chash:10}.{c:ymd}</EVRevisionFormat> <EVRevisionFormat>1.2.7+{chash:10}.{c:ymd}</EVRevisionFormat>
<EVDefault>true</EVDefault> <EVDefault>true</EVDefault>
<EVInfo>true</EVInfo> <EVInfo>true</EVInfo>
<EVTagMatch>v[0-9]*</EVTagMatch> <EVTagMatch>v[0-9]*</EVTagMatch>

View File

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

View File

@@ -112,12 +112,15 @@ internal static class LogHelper
string.IsNullOrWhiteSpace(message)) return; string.IsNullOrWhiteSpace(message)) return;
var logLevel = logType.ToLogLevel(); var logLevel = logType.ToLogLevel();
if (IsLogLevelEnabled(settings, logLevel)) Log(logger, logLevel, message); if (IsLogLevelEnabled(settings, logType))
{
Log(logger, logLevel, message);
}
} }
public static bool IsLogLevelEnabled(LoggerSettings settings, LogLevel logLevel) private static bool IsLogLevelEnabled(LoggerSettings settings, ELogType logType)
{ {
return settings.MinLogType.ToLogLevel() <= logLevel; return settings.MinLogType != ELogType.NONE && settings.MinLogType >= logType;
} }
private static void Log(ILogger logger, LogLevel logLevel, string message) private static void Log(ILogger logger, LogLevel logLevel, string message)