Updated
This commit is contained in:
@@ -69,17 +69,17 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
|
||||
var file = GetFullName(group.Key);
|
||||
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)
|
||||
{
|
||||
AppendMessage(sb, message);
|
||||
AppendMessage(stringBuilder, message);
|
||||
}
|
||||
}
|
||||
|
||||
await FlushAsync(file, sb, token).ConfigureAwait(false);
|
||||
await FlushAsync(file, stringBuilder, token).ConfigureAwait(false);
|
||||
DeleteOldLogFiles();
|
||||
}
|
||||
}
|
||||
@@ -127,9 +127,9 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
|
||||
_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;
|
||||
}
|
||||
@@ -137,22 +137,25 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
|
||||
await _writeLock.WaitAsync(token).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
using var fs = new FileStream(
|
||||
var text = stringBuilder.ToString();
|
||||
byte[] bytesToWrite = Encoding.UTF8.GetBytes(text);
|
||||
|
||||
using (var fileStream = new FileStream(
|
||||
file,
|
||||
FileMode.Append,
|
||||
FileAccess.Write,
|
||||
FileShare.Read,
|
||||
64 * 1024,
|
||||
useAsync: true);
|
||||
useAsync: true))
|
||||
{
|
||||
await fileStream.WriteAsync(bytesToWrite, 0, bytesToWrite.Length, token);
|
||||
}
|
||||
|
||||
using var writer = new StreamWriter(fs, Encoding.UTF8);
|
||||
var text = sb.ToString();
|
||||
await writer.WriteAsync(text).ConfigureAwait(false);
|
||||
|
||||
_fileSizes.AddOrUpdate(file, Encoding.UTF8.GetByteCount(text), (_, old) => old + Encoding.UTF8.GetByteCount(text));
|
||||
// Update current file size correctly
|
||||
_fileSizes.AddOrUpdate(file, bytesToWrite.Length, (_, old) => old + bytesToWrite.Length);
|
||||
_currentFileSize = _fileSizes[file];
|
||||
|
||||
sb.Clear();
|
||||
stringBuilder.Clear();
|
||||
|
||||
if (_fileSizes[file] >= _maxFileSize)
|
||||
{
|
||||
|
||||
@@ -3,9 +3,6 @@ using EonaCat.Logger.Extensions;
|
||||
using EonaCat.Logger.Managers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace EonaCat.Logger.EonaCatCoreLogger.Internal
|
||||
{
|
||||
@@ -25,13 +22,8 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
|
||||
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
||||
}
|
||||
|
||||
private DateTimeOffset Now
|
||||
{
|
||||
get
|
||||
{
|
||||
return _settings.UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
|
||||
}
|
||||
}
|
||||
private DateTimeOffset Now =>
|
||||
_settings.UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state) => NullScope.Instance;
|
||||
|
||||
@@ -49,64 +41,41 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
|
||||
return;
|
||||
}
|
||||
|
||||
string rawMessage;
|
||||
if (exception != null)
|
||||
{
|
||||
rawMessage = exception.FormatExceptionToMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
rawMessage = formatter != null ? formatter(state, null) : string.Empty;
|
||||
}
|
||||
string rawMessage = exception != null
|
||||
? exception.FormatExceptionToMessage()
|
||||
: formatter?.Invoke(state, null);
|
||||
|
||||
if (string.IsNullOrEmpty(rawMessage))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var timestamp = Now;
|
||||
LogInternalAsync(timestamp, logLevel, rawMessage, exception).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task LogInternalAsync(
|
||||
DateTimeOffset timestamp,
|
||||
LogLevel logLevel,
|
||||
string message,
|
||||
Exception exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
var timestamp = Now;
|
||||
|
||||
string formatted = LogHelper.FormatMessageWithHeader(
|
||||
_settings,
|
||||
logLevel.FromLogLevel(),
|
||||
message,
|
||||
rawMessage,
|
||||
timestamp.DateTime,
|
||||
_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,
|
||||
LogType = logLevel.FromLogLevel(),
|
||||
Category = _category,
|
||||
Exception = exception,
|
||||
Origin = string.IsNullOrWhiteSpace(_settings.LogOrigin) ? "BatchingLogger" : _settings.LogOrigin
|
||||
});
|
||||
}
|
||||
DateTime = timestamp.DateTime,
|
||||
Message = writtenMessage,
|
||||
LogType = logLevel.FromLogLevel(),
|
||||
Category = _category,
|
||||
Exception = exception,
|
||||
Origin = string.IsNullOrWhiteSpace(_settings.LogOrigin)
|
||||
? "BatchingLogger"
|
||||
: _settings.LogOrigin
|
||||
});
|
||||
|
||||
await _provider.WriteMessagesAsync(new List<LogMessage>
|
||||
{
|
||||
new LogMessage
|
||||
{
|
||||
Timestamp = timestamp,
|
||||
Message = formatted
|
||||
}
|
||||
}, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -116,7 +85,7 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
|
||||
|
||||
private sealed class NullScope : IDisposable
|
||||
{
|
||||
public static readonly NullScope Instance = new NullScope();
|
||||
public static readonly NullScope Instance = new();
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
|
||||
}
|
||||
|
||||
_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)
|
||||
{
|
||||
@@ -102,23 +102,24 @@ public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
|
||||
private async Task ProcessLoop()
|
||||
{
|
||||
var batch = new List<LogMessage>(_batchSize);
|
||||
var flushInterval = LoggerSettings.FileLoggerOptions.FlushPeriod;
|
||||
var timeoutMs = (int)Math.Min(flushInterval.TotalMilliseconds, int.MaxValue);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (batch.Count > 0)
|
||||
{
|
||||
await FlushBatchAsync(batch);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
@@ -127,10 +128,6 @@ public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
|
||||
await FlushBatchAsync(batch);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task FlushBatchAsync(List<LogMessage> batch)
|
||||
|
||||
@@ -118,43 +118,45 @@ namespace EonaCat.Logger.Managers
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsRunning)
|
||||
{
|
||||
await StartNewLogAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await InternalWriteAsync(CurrentDateTime, message, logType, writeToConsole,
|
||||
customSplunkSourceType, grayLogFacility, grayLogSource, grayLogVersion, disableSplunkSSL)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
private readonly SemaphoreSlim _startLock = new(1, 1);
|
||||
public async Task StartNewLogAsync()
|
||||
{
|
||||
if (_isDisposing || _tokenSource.IsCancellationRequested)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
CreateLogger();
|
||||
|
||||
// Ensure log file exists
|
||||
Directory.CreateDirectory(Settings.FileLoggerOptions.LogDirectory);
|
||||
if (LoggerProvider is FileLoggerProvider fileProvider)
|
||||
finally
|
||||
{
|
||||
fileProvider.InitializeCurrentFile();
|
||||
_startLock.Release();
|
||||
}
|
||||
|
||||
_logDate = CurrentDateTime;
|
||||
IsRunning = true;
|
||||
}
|
||||
|
||||
|
||||
private async Task StopLoggingAsync()
|
||||
{
|
||||
if (_isDisposing)
|
||||
@@ -175,6 +177,12 @@ namespace EonaCat.Logger.Managers
|
||||
|
||||
private void CreateLogger()
|
||||
{
|
||||
if (Logger != null)
|
||||
{
|
||||
// already created
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isDisposing)
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -355,4 +355,9 @@ public class LoggerSettings
|
||||
TypesToLog.Add(ELogType.DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
internal void RaiseOnLog(EonaCatLogMessage eonaCatLogMessage)
|
||||
{
|
||||
OnLogEvent(eonaCatLogMessage);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user