Updated
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user