diff --git a/EonaCat.Logger/EonaCat.Logger.csproj b/EonaCat.Logger/EonaCat.Logger.csproj
index 333e848..93c0780 100644
--- a/EonaCat.Logger/EonaCat.Logger.csproj
+++ b/EonaCat.Logger/EonaCat.Logger.csproj
@@ -3,7 +3,7 @@
.netstandard2.1; net6.0; net7.0; net8.0; net4.8;
icon.ico
latest
- 1.2.6
+ 1.2.7
EonaCat (Jeroen Saey)
true
EonaCat (Jeroen Saey)
@@ -24,7 +24,7 @@
- 1.2.6+{chash:10}.{c:ymd}
+ 1.2.7+{chash:10}.{c:ymd}
true
true
v[0-9]*
diff --git a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs
index cb60394..c2a2f24 100644
--- a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs
+++ b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs
@@ -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 _buffer = new ConcurrentDictionary();
+
+ public event EventHandler OnError;
///
/// Creates an instance of the
@@ -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 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 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();
+
///
- /// Rollover logFiles
+ /// Rollover logFiles
///
- 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 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;
}
+ }
}
///
diff --git a/EonaCat.Logger/Managers/LogHelper.cs b/EonaCat.Logger/Managers/LogHelper.cs
index 9da5bb0..5e766e9 100644
--- a/EonaCat.Logger/Managers/LogHelper.cs
+++ b/EonaCat.Logger/Managers/LogHelper.cs
@@ -112,12 +112,15 @@ internal static class LogHelper
string.IsNullOrWhiteSpace(message)) return;
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)