This commit is contained in:
2026-02-03 20:06:04 +01:00
parent d9454280b9
commit 14d8e96307
5 changed files with 80 additions and 98 deletions

View File

@@ -69,17 +69,17 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
var file = GetFullName(group.Key); var file = GetFullName(group.Key);
InitializeFile(file); InitializeFile(file);
var sb = _buffers.GetOrAdd(file, _ => new StringBuilder(4096)); var stringBuilder = _buffers.GetOrAdd(file, _ => new StringBuilder(4096));
lock (sb) lock (stringBuilder)
{ {
foreach (var message in group) foreach (var message in group)
{ {
AppendMessage(sb, message); AppendMessage(stringBuilder, message);
} }
} }
await FlushAsync(file, sb, token).ConfigureAwait(false); await FlushAsync(file, stringBuilder, token).ConfigureAwait(false);
DeleteOldLogFiles(); DeleteOldLogFiles();
} }
} }
@@ -127,9 +127,9 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
_currentFileSize = File.Exists(_logFile) ? new FileInfo(_logFile).Length : 0; _currentFileSize = File.Exists(_logFile) ? new FileInfo(_logFile).Length : 0;
} }
private async Task FlushAsync(string file, StringBuilder sb, CancellationToken token) private async Task FlushAsync(string file, StringBuilder stringBuilder, CancellationToken token)
{ {
if (sb.Length == 0) if (stringBuilder.Length == 0)
{ {
return; return;
} }
@@ -137,22 +137,25 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
await _writeLock.WaitAsync(token).ConfigureAwait(false); await _writeLock.WaitAsync(token).ConfigureAwait(false);
try try
{ {
using var fs = new FileStream( var text = stringBuilder.ToString();
byte[] bytesToWrite = Encoding.UTF8.GetBytes(text);
using (var fileStream = new FileStream(
file, file,
FileMode.Append, FileMode.Append,
FileAccess.Write, FileAccess.Write,
FileShare.Read, FileShare.Read,
64 * 1024, 64 * 1024,
useAsync: true); useAsync: true))
{
await fileStream.WriteAsync(bytesToWrite, 0, bytesToWrite.Length, token);
}
using var writer = new StreamWriter(fs, Encoding.UTF8); // Update current file size correctly
var text = sb.ToString(); _fileSizes.AddOrUpdate(file, bytesToWrite.Length, (_, old) => old + bytesToWrite.Length);
await writer.WriteAsync(text).ConfigureAwait(false);
_fileSizes.AddOrUpdate(file, Encoding.UTF8.GetByteCount(text), (_, old) => old + Encoding.UTF8.GetByteCount(text));
_currentFileSize = _fileSizes[file]; _currentFileSize = _fileSizes[file];
sb.Clear(); stringBuilder.Clear();
if (_fileSizes[file] >= _maxFileSize) if (_fileSizes[file] >= _maxFileSize)
{ {

View File

@@ -3,9 +3,6 @@ using EonaCat.Logger.Extensions;
using EonaCat.Logger.Managers; using EonaCat.Logger.Managers;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace EonaCat.Logger.EonaCatCoreLogger.Internal namespace EonaCat.Logger.EonaCatCoreLogger.Internal
{ {
@@ -25,13 +22,8 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
_settings = settings ?? throw new ArgumentNullException(nameof(settings)); _settings = settings ?? throw new ArgumentNullException(nameof(settings));
} }
private DateTimeOffset Now private DateTimeOffset Now =>
{ _settings.UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
get
{
return _settings.UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
}
}
public IDisposable BeginScope<TState>(TState state) => NullScope.Instance; public IDisposable BeginScope<TState>(TState state) => NullScope.Instance;
@@ -49,64 +41,41 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
return; return;
} }
string rawMessage; string rawMessage = exception != null
if (exception != null) ? exception.FormatExceptionToMessage()
{ : formatter?.Invoke(state, null);
rawMessage = exception.FormatExceptionToMessage();
}
else
{
rawMessage = formatter != null ? formatter(state, null) : string.Empty;
}
if (string.IsNullOrEmpty(rawMessage)) if (string.IsNullOrEmpty(rawMessage))
{ {
return; return;
} }
var timestamp = Now;
LogInternalAsync(timestamp, logLevel, rawMessage, exception).ConfigureAwait(false);
}
private async Task LogInternalAsync(
DateTimeOffset timestamp,
LogLevel logLevel,
string message,
Exception exception)
{
try try
{ {
var timestamp = Now;
string formatted = LogHelper.FormatMessageWithHeader( string formatted = LogHelper.FormatMessageWithHeader(
_settings, _settings,
logLevel.FromLogLevel(), logLevel.FromLogLevel(),
message, rawMessage,
timestamp.DateTime, timestamp.DateTime,
_category); _category);
var writtenMessage = _provider.AddMessage(timestamp, formatted, _category); var writtenMessage = _provider.AddMessage(timestamp, formatted, _category);
var onLogEvent = _settings.OnLogEvent;
if (onLogEvent != null) _settings.RaiseOnLog(new EonaCatLogMessage
{ {
onLogEvent(new EonaCatLogMessage DateTime = timestamp.DateTime,
{ Message = writtenMessage,
DateTime = timestamp.DateTime, LogType = logLevel.FromLogLevel(),
Message = writtenMessage, Category = _category,
LogType = logLevel.FromLogLevel(), Exception = exception,
Category = _category, Origin = string.IsNullOrWhiteSpace(_settings.LogOrigin)
Exception = exception, ? "BatchingLogger"
Origin = string.IsNullOrWhiteSpace(_settings.LogOrigin) ? "BatchingLogger" : _settings.LogOrigin : _settings.LogOrigin
}); });
}
await _provider.WriteMessagesAsync(new List<LogMessage>
{
new LogMessage
{
Timestamp = timestamp,
Message = formatted
}
}, CancellationToken.None).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -116,7 +85,7 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
private sealed class NullScope : IDisposable private sealed class NullScope : IDisposable
{ {
public static readonly NullScope Instance = new NullScope(); public static readonly NullScope Instance = new();
public void Dispose() { } public void Dispose() { }
} }
} }

View File

@@ -30,7 +30,7 @@ public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
} }
_batchSize = currentOptions.BatchSize > 0 ? currentOptions.BatchSize : 100; _batchSize = currentOptions.BatchSize > 0 ? currentOptions.BatchSize : 100;
_queue = new BlockingCollection<LogMessage>(new ConcurrentQueue<LogMessage>()); _queue = new BlockingCollection<LogMessage>(Math.Max(1, _batchSize * 2));
if (currentOptions is FileLoggerOptions file) if (currentOptions is FileLoggerOptions file)
{ {
@@ -102,23 +102,24 @@ public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
private async Task ProcessLoop() private async Task ProcessLoop()
{ {
var batch = new List<LogMessage>(_batchSize); var batch = new List<LogMessage>(_batchSize);
var flushInterval = LoggerSettings.FileLoggerOptions.FlushPeriod;
var timeoutMs = (int)Math.Min(flushInterval.TotalMilliseconds, int.MaxValue);
try try
{ {
foreach (var item in _queue.GetConsumingEnumerable(_cts.Token)) while (!_cts.Token.IsCancellationRequested)
{ {
batch.Add(item); if (_queue.TryTake(out var item, timeoutMs, _cts.Token))
{
batch.Add(item);
}
if (batch.Count >= _batchSize) if (batch.Count >= _batchSize ||
(batch.Count > 0 && !_queue.TryTake(out _, 0)))
{ {
await FlushBatchAsync(batch); await FlushBatchAsync(batch);
} }
} }
if (batch.Count > 0)
{
await FlushBatchAsync(batch);
}
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@@ -127,10 +128,6 @@ public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
await FlushBatchAsync(batch); await FlushBatchAsync(batch);
} }
} }
catch (Exception ex)
{
Console.Error.WriteLine(ex);
}
} }
private async Task FlushBatchAsync(List<LogMessage> batch) private async Task FlushBatchAsync(List<LogMessage> batch)

View File

@@ -118,43 +118,45 @@ namespace EonaCat.Logger.Managers
return; return;
} }
if (!IsRunning)
{
await StartNewLogAsync().ConfigureAwait(false);
}
await InternalWriteAsync(CurrentDateTime, message, logType, writeToConsole, await InternalWriteAsync(CurrentDateTime, message, logType, writeToConsole,
customSplunkSourceType, grayLogFacility, grayLogSource, grayLogVersion, disableSplunkSSL) customSplunkSourceType, grayLogFacility, grayLogSource, grayLogVersion, disableSplunkSSL)
.ConfigureAwait(false); .ConfigureAwait(false);
} }
private readonly SemaphoreSlim _startLock = new(1, 1);
public async Task StartNewLogAsync() public async Task StartNewLogAsync()
{ {
if (_isDisposing || _tokenSource.IsCancellationRequested) if (_isDisposing || _tokenSource.IsCancellationRequested)
{
return; return;
}
if (IsRunning && CurrentDateTime.Date > _logDate.Date) await _startLock.WaitAsync().ConfigureAwait(false);
try
{ {
await StopLoggingAsync().ConfigureAwait(false); if (IsRunning && CurrentDateTime.Date > _logDate.Date)
{
await StopLoggingAsync().ConfigureAwait(false);
}
if (!IsRunning)
{
CreateLogger();
Directory.CreateDirectory(Settings.FileLoggerOptions.LogDirectory);
if (LoggerProvider is FileLoggerProvider fileProvider)
{
fileProvider.InitializeCurrentFile();
}
_logDate = CurrentDateTime;
IsRunning = true;
}
} }
finally
CreateLogger();
// Ensure log file exists
Directory.CreateDirectory(Settings.FileLoggerOptions.LogDirectory);
if (LoggerProvider is FileLoggerProvider fileProvider)
{ {
fileProvider.InitializeCurrentFile(); _startLock.Release();
} }
_logDate = CurrentDateTime;
IsRunning = true;
} }
private async Task StopLoggingAsync() private async Task StopLoggingAsync()
{ {
if (_isDisposing) if (_isDisposing)
@@ -175,6 +177,12 @@ namespace EonaCat.Logger.Managers
private void CreateLogger() private void CreateLogger()
{ {
if (Logger != null)
{
// already created
return;
}
if (_isDisposing) if (_isDisposing)
{ {
return; return;

View File

@@ -355,4 +355,9 @@ public class LoggerSettings
TypesToLog.Add(ELogType.DEBUG); TypesToLog.Add(ELogType.DEBUG);
} }
} }
internal void RaiseOnLog(EonaCatLogMessage eonaCatLogMessage)
{
OnLogEvent(eonaCatLogMessage);
}
} }