(bugfix): fillelogger multithreading issue
This commit is contained in:
parent
9f9d5fc474
commit
016904f333
@ -1,14 +1,14 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using MaksIT.Core.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
||||
namespace MaksIT.Core.Logging;
|
||||
|
||||
public abstract class BaseFileLogger : ILogger, IDisposable {
|
||||
private readonly LockManager _lockManager = new LockManager();
|
||||
private readonly string _folderPath;
|
||||
private readonly TimeSpan _retentionPeriod;
|
||||
private static readonly Mutex _fileMutex = new Mutex(false, "Global\\MaksITLoggerFileMutex"); // Named mutex for cross-process locking
|
||||
|
||||
protected BaseFileLogger(string folderPath, TimeSpan retentionPeriod) {
|
||||
_folderPath = folderPath;
|
||||
@ -22,17 +22,30 @@ public abstract class BaseFileLogger : ILogger, IDisposable {
|
||||
return logLevel != LogLevel.None;
|
||||
}
|
||||
|
||||
public abstract void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter);
|
||||
public abstract Task Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter);
|
||||
|
||||
// Explicit interface implementation for ILogger.Log (void)
|
||||
void ILogger.Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) {
|
||||
// Call the async Log and wait synchronously for compatibility
|
||||
Log(logLevel, eventId, state, exception, formatter).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
protected string GenerateLogFileName(string extension) {
|
||||
return Path.Combine(_folderPath, $"log_{DateTime.UtcNow:yyyy-MM-dd}.{extension}");
|
||||
}
|
||||
|
||||
protected void AppendToLogFile(string logFileName, string content) {
|
||||
_lockManager.ExecuteWithLockAsync(async () => {
|
||||
await File.AppendAllTextAsync(logFileName, content);
|
||||
RemoveExpiredLogFiles(Path.GetExtension(logFileName));
|
||||
}).Wait();
|
||||
protected Task AppendToLogFileAsync(string logFileName, string content) {
|
||||
bool mutexAcquired = false;
|
||||
try {
|
||||
mutexAcquired = _fileMutex.WaitOne(10000);
|
||||
if (!mutexAcquired) throw new IOException("Could not acquire file mutex for logging.");
|
||||
File.AppendAllText(logFileName, content); // Synchronous write
|
||||
RemoveExpiredLogFiles(Path.GetExtension(logFileName));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
finally {
|
||||
if (mutexAcquired) _fileMutex.ReleaseMutex();
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveExpiredLogFiles(string extension) {
|
||||
@ -53,5 +66,11 @@ public abstract class BaseFileLogger : ILogger, IDisposable {
|
||||
|
||||
public void Dispose() {
|
||||
_lockManager.Dispose();
|
||||
// Do NOT dispose the static mutex here; it should be disposed once per process, not per instance.
|
||||
}
|
||||
|
||||
// Optionally, add a static method to dispose the mutex at application shutdown:
|
||||
public static void DisposeMutex() {
|
||||
_fileMutex?.Dispose();
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@ namespace MaksIT.Core.Logging;
|
||||
public class FileLogger : BaseFileLogger {
|
||||
public FileLogger(string folderPath, TimeSpan retentionPeriod) : base(folderPath, retentionPeriod) { }
|
||||
|
||||
public override void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) {
|
||||
public override async Task Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) {
|
||||
if (!IsEnabled(logLevel))
|
||||
return;
|
||||
|
||||
@ -13,12 +13,12 @@ public class FileLogger : BaseFileLogger {
|
||||
if (string.IsNullOrEmpty(message))
|
||||
return;
|
||||
|
||||
var logRecord = $"{DateTime.UtcNow.ToString("o")} [{logLevel}] {message}";
|
||||
var logRecord = $"{DateTime.UtcNow.ToString("o")}" + $" [{logLevel}] {message}";
|
||||
if (exception != null) {
|
||||
logRecord += Environment.NewLine + exception;
|
||||
}
|
||||
|
||||
var logFileName = GenerateLogFileName("txt");
|
||||
AppendToLogFile(logFileName, logRecord + Environment.NewLine);
|
||||
await AppendToLogFileAsync(logFileName, logRecord + Environment.NewLine);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ namespace MaksIT.Core.Logging;
|
||||
public class JsonFileLogger : BaseFileLogger {
|
||||
public JsonFileLogger(string folderPath, TimeSpan retentionPeriod) : base(folderPath, retentionPeriod) { }
|
||||
|
||||
public override void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) {
|
||||
public override async Task Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) {
|
||||
if (!IsEnabled(logLevel))
|
||||
return;
|
||||
|
||||
@ -18,6 +18,6 @@ public class JsonFileLogger : BaseFileLogger {
|
||||
};
|
||||
|
||||
var logFileName = GenerateLogFileName("json");
|
||||
AppendToLogFile(logFileName, JsonSerializer.Serialize(logEntry) + Environment.NewLine);
|
||||
await AppendToLogFileAsync(logFileName, JsonSerializer.Serialize(logEntry) + Environment.NewLine);
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
<!-- NuGet package metadata -->
|
||||
<PackageId>MaksIT.Core</PackageId>
|
||||
<Version>1.5.9</Version>
|
||||
<Version>1.6.0</Version>
|
||||
<Authors>Maksym Sadovnychyy</Authors>
|
||||
<Company>MAKS-IT</Company>
|
||||
<Product>MaksIT.Core</Product>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user