(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 MaksIT.Core.Threading;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
|
||||||
namespace MaksIT.Core.Logging;
|
namespace MaksIT.Core.Logging;
|
||||||
|
|
||||||
public abstract class BaseFileLogger : ILogger, IDisposable {
|
public abstract class BaseFileLogger : ILogger, IDisposable {
|
||||||
private readonly LockManager _lockManager = new LockManager();
|
private readonly LockManager _lockManager = new LockManager();
|
||||||
private readonly string _folderPath;
|
private readonly string _folderPath;
|
||||||
private readonly TimeSpan _retentionPeriod;
|
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) {
|
protected BaseFileLogger(string folderPath, TimeSpan retentionPeriod) {
|
||||||
_folderPath = folderPath;
|
_folderPath = folderPath;
|
||||||
@ -22,17 +22,30 @@ public abstract class BaseFileLogger : ILogger, IDisposable {
|
|||||||
return logLevel != LogLevel.None;
|
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) {
|
protected string GenerateLogFileName(string extension) {
|
||||||
return Path.Combine(_folderPath, $"log_{DateTime.UtcNow:yyyy-MM-dd}.{extension}");
|
return Path.Combine(_folderPath, $"log_{DateTime.UtcNow:yyyy-MM-dd}.{extension}");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void AppendToLogFile(string logFileName, string content) {
|
protected Task AppendToLogFileAsync(string logFileName, string content) {
|
||||||
_lockManager.ExecuteWithLockAsync(async () => {
|
bool mutexAcquired = false;
|
||||||
await File.AppendAllTextAsync(logFileName, content);
|
try {
|
||||||
RemoveExpiredLogFiles(Path.GetExtension(logFileName));
|
mutexAcquired = _fileMutex.WaitOne(10000);
|
||||||
}).Wait();
|
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) {
|
private void RemoveExpiredLogFiles(string extension) {
|
||||||
@ -53,5 +66,11 @@ public abstract class BaseFileLogger : ILogger, IDisposable {
|
|||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
_lockManager.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 class FileLogger : BaseFileLogger {
|
||||||
public FileLogger(string folderPath, TimeSpan retentionPeriod) : base(folderPath, retentionPeriod) { }
|
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))
|
if (!IsEnabled(logLevel))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -13,12 +13,12 @@ public class FileLogger : BaseFileLogger {
|
|||||||
if (string.IsNullOrEmpty(message))
|
if (string.IsNullOrEmpty(message))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var logRecord = $"{DateTime.UtcNow.ToString("o")} [{logLevel}] {message}";
|
var logRecord = $"{DateTime.UtcNow.ToString("o")}" + $" [{logLevel}] {message}";
|
||||||
if (exception != null) {
|
if (exception != null) {
|
||||||
logRecord += Environment.NewLine + exception;
|
logRecord += Environment.NewLine + exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
var logFileName = GenerateLogFileName("txt");
|
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 class JsonFileLogger : BaseFileLogger {
|
||||||
public JsonFileLogger(string folderPath, TimeSpan retentionPeriod) : base(folderPath, retentionPeriod) { }
|
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))
|
if (!IsEnabled(logLevel))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -18,6 +18,6 @@ public class JsonFileLogger : BaseFileLogger {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var logFileName = GenerateLogFileName("json");
|
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 -->
|
<!-- NuGet package metadata -->
|
||||||
<PackageId>MaksIT.Core</PackageId>
|
<PackageId>MaksIT.Core</PackageId>
|
||||||
<Version>1.5.9</Version>
|
<Version>1.6.0</Version>
|
||||||
<Authors>Maksym Sadovnychyy</Authors>
|
<Authors>Maksym Sadovnychyy</Authors>
|
||||||
<Company>MAKS-IT</Company>
|
<Company>MAKS-IT</Company>
|
||||||
<Product>MaksIT.Core</Product>
|
<Product>MaksIT.Core</Product>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user