Updated
This commit is contained in:
@@ -13,8 +13,8 @@
|
|||||||
<Copyright>EonaCat (Jeroen Saey)</Copyright>
|
<Copyright>EonaCat (Jeroen Saey)</Copyright>
|
||||||
<PackageTags>EonaCat;Logger;EonaCatLogger;Log;Writer;Jeroen;Saey</PackageTags>
|
<PackageTags>EonaCat;Logger;EonaCatLogger;Log;Writer;Jeroen;Saey</PackageTags>
|
||||||
<PackageIconUrl />
|
<PackageIconUrl />
|
||||||
<Version>1.6.1</Version>
|
<Version>1.6.2</Version>
|
||||||
<FileVersion>1.6.1</FileVersion>
|
<FileVersion>1.6.2</FileVersion>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<EVRevisionFormat>1.6.1+{chash:10}.{c:ymd}</EVRevisionFormat>
|
<EVRevisionFormat>1.6.2+{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>
|
||||||
|
|||||||
@@ -13,9 +13,6 @@ using Microsoft.Extensions.Options;
|
|||||||
|
|
||||||
namespace EonaCat.Logger.EonaCatCoreLogger;
|
namespace EonaCat.Logger.EonaCatCoreLogger;
|
||||||
|
|
||||||
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
|
||||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
|
||||||
|
|
||||||
[ProviderAlias("EonaCatFileLogger")]
|
[ProviderAlias("EonaCatFileLogger")]
|
||||||
public sealed class FileLoggerProvider : BatchingLoggerProvider
|
public sealed class FileLoggerProvider : BatchingLoggerProvider
|
||||||
{
|
{
|
||||||
@@ -25,19 +22,20 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
|
|||||||
private readonly int _maxRetainedFiles;
|
private readonly int _maxRetainedFiles;
|
||||||
private readonly int _maxRolloverFiles;
|
private readonly int _maxRolloverFiles;
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, StringBuilder> _buffer = new();
|
|
||||||
private readonly SemaphoreSlim _writeLock = new(1, 1);
|
|
||||||
private readonly SemaphoreSlim _rolloverLock = new(1, 1);
|
|
||||||
private readonly LoggerScopedContext _context = new();
|
|
||||||
|
|
||||||
private string _logFile;
|
private string _logFile;
|
||||||
private long _currentFileSize;
|
private long _currentFileSize;
|
||||||
private int _isWriting;
|
|
||||||
|
private readonly ConcurrentDictionary<string, StringBuilder> _buffers = new();
|
||||||
|
private readonly ConcurrentDictionary<string, long> _fileSizes = new();
|
||||||
|
private readonly SemaphoreSlim _writeLock = new(1, 1);
|
||||||
|
private readonly SemaphoreSlim _rolloverLock = new(1, 1);
|
||||||
|
|
||||||
|
private readonly LoggerScopedContext _context = new();
|
||||||
|
|
||||||
|
public string LogFile => _logFile ?? string.Empty;
|
||||||
|
|
||||||
public event EventHandler<ErrorMessage> OnError;
|
public event EventHandler<ErrorMessage> OnError;
|
||||||
|
|
||||||
public string LogFile => _logFile;
|
|
||||||
|
|
||||||
public FileLoggerProvider(IOptions<FileLoggerOptions> options) : base(options)
|
public FileLoggerProvider(IOptions<FileLoggerOptions> options) : base(options)
|
||||||
{
|
{
|
||||||
var o = options.Value;
|
var o = options.Value;
|
||||||
@@ -47,68 +45,56 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
|
|||||||
_maxRetainedFiles = o.RetainedFileCountLimit;
|
_maxRetainedFiles = o.RetainedFileCountLimit;
|
||||||
_maxRolloverFiles = o.MaxRolloverFiles;
|
_maxRolloverFiles = o.MaxRolloverFiles;
|
||||||
IncludeCorrelationId = o.IncludeCorrelationId;
|
IncludeCorrelationId = o.IncludeCorrelationId;
|
||||||
|
|
||||||
|
Directory.CreateDirectory(_path);
|
||||||
|
InitializeCurrentFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IncludeCorrelationId { get; }
|
public bool IncludeCorrelationId { get; }
|
||||||
|
|
||||||
protected override async Task WriteMessagesAsync(
|
internal override async Task WriteMessagesAsync(
|
||||||
IReadOnlyList<LogMessage> messages,
|
IReadOnlyList<LogMessage> messages,
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
if (Interlocked.Exchange(ref _isWriting, 1) == 1)
|
if (messages.Count == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
Directory.CreateDirectory(_path);
|
||||||
|
|
||||||
|
// Group messages by date
|
||||||
|
foreach (var group in messages.GroupBy(m => (m.Timestamp.Year, m.Timestamp.Month, m.Timestamp.Day)))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(_path);
|
var file = GetFullName(group.Key);
|
||||||
|
InitializeFile(file);
|
||||||
|
|
||||||
foreach (var group in messages.GroupBy(GetGrouping))
|
var sb = _buffers.GetOrAdd(file, _ => new StringBuilder(4096));
|
||||||
|
|
||||||
|
lock (sb)
|
||||||
{
|
{
|
||||||
var file = GetFullName(group.Key);
|
foreach (var message in group)
|
||||||
InitializeFile(file);
|
|
||||||
|
|
||||||
var stringBuilder = _buffer.GetOrAdd(file, _ => new StringBuilder(4096));
|
|
||||||
lock (stringBuilder)
|
|
||||||
{
|
{
|
||||||
foreach (var message in group)
|
AppendMessage(sb, message);
|
||||||
{
|
|
||||||
AppendMessage(stringBuilder, message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await FlushAsync(file, stringBuilder, token).ConfigureAwait(false);
|
|
||||||
DeleteOldLogFiles();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (Exception ex)
|
await FlushAsync(file, sb, token).ConfigureAwait(false);
|
||||||
{
|
DeleteOldLogFiles();
|
||||||
OnError?.Invoke(this, new ErrorMessage
|
|
||||||
{
|
|
||||||
Exception = ex,
|
|
||||||
Message = "Failed to write log file"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Interlocked.Exchange(ref _isWriting, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AppendMessage(StringBuilder sb, LogMessage msg)
|
private void AppendMessage(StringBuilder sb, LogMessage msg)
|
||||||
{
|
{
|
||||||
// Ensure correlation id exists (once per scope)
|
// Ensure correlation id exists (once per async context)
|
||||||
if (IncludeCorrelationId)
|
if (IncludeCorrelationId)
|
||||||
{
|
{
|
||||||
var cid = _context.Get("CorrelationId") ?? Guid.NewGuid().ToString();
|
var cid = _context.Get("CorrelationId") ?? Guid.NewGuid().ToString();
|
||||||
_context.Set("CorrelationId", cid);
|
_context.Set("CorrelationId", cid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Append the already-formatted message FIRST
|
|
||||||
sb.Append(msg.Message);
|
sb.Append(msg.Message);
|
||||||
|
|
||||||
// 2. Append context AFTER the message
|
|
||||||
var ctx = _context.GetAll();
|
var ctx = _context.GetAll();
|
||||||
if (ctx.Count > 0)
|
if (ctx.Count > 0)
|
||||||
{
|
{
|
||||||
@@ -121,19 +107,26 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
|
|||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.Append(kv.Key)
|
sb.Append(kv.Key).Append('=').Append(kv.Value);
|
||||||
.Append('=')
|
|
||||||
.Append(kv.Value);
|
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
sb.Append(']');
|
sb.Append(']');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. End the line
|
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void InitializeCurrentFile()
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(_logFile))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logFile = GetFullName((DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day));
|
||||||
|
_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 sb, CancellationToken token)
|
||||||
{
|
{
|
||||||
if (sb.Length == 0)
|
if (sb.Length == 0)
|
||||||
@@ -152,14 +145,16 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
|
|||||||
64 * 1024,
|
64 * 1024,
|
||||||
useAsync: true);
|
useAsync: true);
|
||||||
|
|
||||||
using var writer = new StreamWriter(fs);
|
using var writer = new StreamWriter(fs, Encoding.UTF8);
|
||||||
var text = sb.ToString();
|
var text = sb.ToString();
|
||||||
await writer.WriteAsync(text).ConfigureAwait(false);
|
await writer.WriteAsync(text).ConfigureAwait(false);
|
||||||
|
|
||||||
_currentFileSize += Encoding.UTF8.GetByteCount(text);
|
_fileSizes.AddOrUpdate(file, Encoding.UTF8.GetByteCount(text), (_, old) => old + Encoding.UTF8.GetByteCount(text));
|
||||||
|
_currentFileSize = _fileSizes[file];
|
||||||
|
|
||||||
sb.Clear();
|
sb.Clear();
|
||||||
|
|
||||||
if (_currentFileSize >= _maxFileSize)
|
if (_fileSizes[file] >= _maxFileSize)
|
||||||
{
|
{
|
||||||
await RollOverAsync(file).ConfigureAwait(false);
|
await RollOverAsync(file).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -195,8 +190,13 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
File.Move(file, Path.Combine(dir, $"{name}.1{ext}"));
|
var firstRoll = Path.Combine(dir, $"{name}.1{ext}");
|
||||||
_currentFileSize = 0;
|
if (File.Exists(file))
|
||||||
|
{
|
||||||
|
File.Move(file, firstRoll);
|
||||||
|
}
|
||||||
|
|
||||||
|
_fileSizes[file] = 0;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -206,15 +206,8 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
|
|||||||
|
|
||||||
private void InitializeFile(string file)
|
private void InitializeFile(string file)
|
||||||
{
|
{
|
||||||
if (_logFile == file)
|
_fileSizes.TryAdd(file, File.Exists(file) ? new FileInfo(file).Length : 0);
|
||||||
{
|
_buffers.TryAdd(file, new StringBuilder(4096));
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_logFile = file;
|
|
||||||
_currentFileSize = File.Exists(file)
|
|
||||||
? new FileInfo(file).Length
|
|
||||||
: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private (int Year, int Month, int Day) GetGrouping(LogMessage m) =>
|
private (int Year, int Month, int Day) GetGrouping(LogMessage m) =>
|
||||||
@@ -225,7 +218,7 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
|
|||||||
? Path.Combine(_path, $"{g.Year:0000}{g.Month:00}{g.Day:00}.log")
|
? Path.Combine(_path, $"{g.Year:0000}{g.Month:00}{g.Day:00}.log")
|
||||||
: Path.Combine(_path, $"{_fileNamePrefix}_{g.Year:0000}{g.Month:00}{g.Day:00}.log");
|
: Path.Combine(_path, $"{_fileNamePrefix}_{g.Year:0000}{g.Month:00}{g.Day:00}.log");
|
||||||
|
|
||||||
protected void DeleteOldLogFiles()
|
private void DeleteOldLogFiles()
|
||||||
{
|
{
|
||||||
if (_maxRetainedFiles <= 0)
|
if (_maxRetainedFiles <= 0)
|
||||||
{
|
{
|
||||||
@@ -234,13 +227,24 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider
|
|||||||
|
|
||||||
var files = new DirectoryInfo(_path)
|
var files = new DirectoryInfo(_path)
|
||||||
.GetFiles($"{_fileNamePrefix}*")
|
.GetFiles($"{_fileNamePrefix}*")
|
||||||
.Where(f => !f.FullName.Equals(_logFile, StringComparison.OrdinalIgnoreCase))
|
.OrderByDescending(f =>
|
||||||
.OrderByDescending(f => f.CreationTimeUtc)
|
{
|
||||||
|
// Parse date from filename instead of CreationTimeUtc
|
||||||
|
var name = Path.GetFileNameWithoutExtension(f.Name);
|
||||||
|
var parts = name.Split('_');
|
||||||
|
var datePart = parts.Length > 1 ? parts[1] : parts[0];
|
||||||
|
if (DateTime.TryParseExact(datePart, "yyyyMMdd", null, System.Globalization.DateTimeStyles.None, out var dt))
|
||||||
|
{
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DateTime.MinValue;
|
||||||
|
})
|
||||||
.Skip(_maxRetainedFiles);
|
.Skip(_maxRetainedFiles);
|
||||||
|
|
||||||
foreach (var f in files)
|
foreach (var f in files)
|
||||||
{
|
{
|
||||||
f.Delete();
|
try { f.Delete(); } catch { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
using System;
|
using EonaCat.Logger.EonaCatCoreLogger.Models;
|
||||||
using EonaCat.Logger.EonaCatCoreLogger.Models;
|
|
||||||
using EonaCat.Logger.Extensions;
|
using EonaCat.Logger.Extensions;
|
||||||
using EonaCat.Logger.Managers;
|
using EonaCat.Logger.Managers;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace EonaCat.Logger.EonaCatCoreLogger.Internal
|
namespace EonaCat.Logger.EonaCatCoreLogger.Internal
|
||||||
{
|
{
|
||||||
@@ -26,7 +29,6 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
// Avoid DateTimeOffset.Now if UseLocalTime is false
|
|
||||||
return _settings.UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
|
return _settings.UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,36 +65,52 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal
|
|||||||
}
|
}
|
||||||
|
|
||||||
var timestamp = Now;
|
var timestamp = Now;
|
||||||
LogInternal(timestamp, logLevel, rawMessage, exception);
|
LogInternalAsync(timestamp, logLevel, rawMessage, exception).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LogInternal(
|
private async Task LogInternalAsync(
|
||||||
DateTimeOffset timestamp,
|
DateTimeOffset timestamp,
|
||||||
LogLevel logLevel,
|
LogLevel logLevel,
|
||||||
string message,
|
string message,
|
||||||
Exception exception)
|
Exception exception)
|
||||||
{
|
{
|
||||||
string formatted = LogHelper.FormatMessageWithHeader(
|
try
|
||||||
_settings,
|
|
||||||
logLevel.FromLogLevel(),
|
|
||||||
message,
|
|
||||||
timestamp.DateTime,
|
|
||||||
_category);
|
|
||||||
|
|
||||||
var writtenMessage = _provider.AddMessage(timestamp, formatted, _category);
|
|
||||||
var onLogEvent = _settings.OnLogEvent;
|
|
||||||
|
|
||||||
if (onLogEvent != null)
|
|
||||||
{
|
{
|
||||||
onLogEvent(new EonaCatLogMessage
|
string formatted = LogHelper.FormatMessageWithHeader(
|
||||||
|
_settings,
|
||||||
|
logLevel.FromLogLevel(),
|
||||||
|
message,
|
||||||
|
timestamp.DateTime,
|
||||||
|
_category);
|
||||||
|
|
||||||
|
var writtenMessage = _provider.AddMessage(timestamp, formatted, _category);
|
||||||
|
var onLogEvent = _settings.OnLogEvent;
|
||||||
|
|
||||||
|
if (onLogEvent != null)
|
||||||
{
|
{
|
||||||
DateTime = timestamp.DateTime,
|
onLogEvent(new EonaCatLogMessage
|
||||||
Message = writtenMessage,
|
{
|
||||||
LogType = logLevel.FromLogLevel(),
|
DateTime = timestamp.DateTime,
|
||||||
Category = _category,
|
Message = writtenMessage,
|
||||||
Exception = exception,
|
LogType = logLevel.FromLogLevel(),
|
||||||
Origin = string.IsNullOrWhiteSpace(_settings.LogOrigin) ? "BatchingLogger" : _settings.LogOrigin
|
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)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Logging error: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,17 +22,17 @@ public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
|
|||||||
|
|
||||||
protected BatchingLoggerProvider(IOptions<BatchingLoggerOptions> options)
|
protected BatchingLoggerProvider(IOptions<BatchingLoggerOptions> options)
|
||||||
{
|
{
|
||||||
var o = options.Value ?? throw new ArgumentNullException(nameof(options));
|
var currentOptions = options.Value ?? throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
if (o.FlushPeriod <= TimeSpan.Zero)
|
if (currentOptions.FlushPeriod <= TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
throw new ArgumentOutOfRangeException(nameof(o.FlushPeriod));
|
throw new ArgumentOutOfRangeException(nameof(currentOptions.FlushPeriod));
|
||||||
}
|
}
|
||||||
|
|
||||||
_batchSize = o.BatchSize > 0 ? o.BatchSize : 100;
|
_batchSize = currentOptions.BatchSize > 0 ? currentOptions.BatchSize : 100;
|
||||||
_queue = new BlockingCollection<LogMessage>(new ConcurrentQueue<LogMessage>());
|
_queue = new BlockingCollection<LogMessage>(new ConcurrentQueue<LogMessage>());
|
||||||
|
|
||||||
if (o is FileLoggerOptions file)
|
if (currentOptions is FileLoggerOptions file)
|
||||||
{
|
{
|
||||||
UseLocalTime = file.UseLocalTime;
|
UseLocalTime = file.UseLocalTime;
|
||||||
UseMask = file.UseMask;
|
UseMask = file.UseMask;
|
||||||
@@ -72,7 +72,7 @@ public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
|
|||||||
public ILogger CreateLogger(string categoryName)
|
public ILogger CreateLogger(string categoryName)
|
||||||
=> new BatchingLogger(this, categoryName, LoggerSettings);
|
=> new BatchingLogger(this, categoryName, LoggerSettings);
|
||||||
|
|
||||||
protected abstract Task WriteMessagesAsync(
|
internal abstract Task WriteMessagesAsync(
|
||||||
IReadOnlyList<LogMessage> messages,
|
IReadOnlyList<LogMessage> messages,
|
||||||
CancellationToken token);
|
CancellationToken token);
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void ProcessLoop()
|
private async Task ProcessLoop()
|
||||||
{
|
{
|
||||||
var batch = new List<LogMessage>(_batchSize);
|
var batch = new List<LogMessage>(_batchSize);
|
||||||
|
|
||||||
@@ -109,32 +109,45 @@ public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable
|
|||||||
{
|
{
|
||||||
batch.Add(item);
|
batch.Add(item);
|
||||||
|
|
||||||
if (batch.Count < _batchSize)
|
if (batch.Count >= _batchSize)
|
||||||
{
|
{
|
||||||
continue;
|
await FlushBatchAsync(batch);
|
||||||
}
|
}
|
||||||
|
|
||||||
await WriteMessagesAsync(batch, _cts.Token).ConfigureAwait(false);
|
|
||||||
batch.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (batch.Count > 0)
|
if (batch.Count > 0)
|
||||||
{
|
{
|
||||||
await WriteMessagesAsync(batch, _cts.Token).ConfigureAwait(false);
|
await FlushBatchAsync(batch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
// normal shutdown
|
if (batch.Count > 0)
|
||||||
|
{
|
||||||
|
await FlushBatchAsync(batch);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// last-resort logging
|
|
||||||
Console.Error.WriteLine(ex);
|
Console.Error.WriteLine(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task FlushBatchAsync(List<LogMessage> batch)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await WriteMessagesAsync(batch, _cts.Token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("An error occurred while processing log batches.", ex);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
batch.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -48,7 +48,10 @@ namespace EonaCat.Logger.EonaCatCoreLogger
|
|||||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
|
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
|
||||||
Exception exception, Func<TState, Exception, string> formatter)
|
Exception exception, Func<TState, Exception, string> formatter)
|
||||||
{
|
{
|
||||||
if (!IsEnabled(logLevel) || formatter == null) return;
|
if (!IsEnabled(logLevel) || formatter == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -55,9 +55,23 @@ namespace EonaCat.Logger.Managers
|
|||||||
public ILogger Logger { get; private set; }
|
public ILogger Logger { get; private set; }
|
||||||
public bool IsRunning { get; private set; }
|
public bool IsRunning { get; private set; }
|
||||||
|
|
||||||
public string CurrentLogFile => LoggerProvider is FileLoggerProvider fileLoggerProvider
|
public string CurrentLogFile
|
||||||
? fileLoggerProvider.LogFile
|
{
|
||||||
: string.Empty;
|
get
|
||||||
|
{
|
||||||
|
if (LoggerProvider is FileLoggerProvider fileLoggerProvider)
|
||||||
|
{
|
||||||
|
// Ensure log file is initialized
|
||||||
|
if (string.IsNullOrEmpty(fileLoggerProvider.LogFile))
|
||||||
|
{
|
||||||
|
fileLoggerProvider.InitializeCurrentFile();
|
||||||
|
}
|
||||||
|
return fileLoggerProvider.LogFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private DateTime CurrentDateTime => Settings.UseLocalTime ? DateTime.Now : DateTime.UtcNow;
|
private DateTime CurrentDateTime => Settings.UseLocalTime ? DateTime.Now : DateTime.UtcNow;
|
||||||
|
|
||||||
@@ -104,11 +118,17 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task StartNewLogAsync()
|
public async Task StartNewLogAsync()
|
||||||
{
|
{
|
||||||
if (_isDisposing || _tokenSource.IsCancellationRequested)
|
if (_isDisposing || _tokenSource.IsCancellationRequested)
|
||||||
@@ -121,10 +141,17 @@ namespace EonaCat.Logger.Managers
|
|||||||
await StopLoggingAsync().ConfigureAwait(false);
|
await StopLoggingAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
IsRunning = true;
|
|
||||||
CreateLogger();
|
CreateLogger();
|
||||||
|
|
||||||
|
// Ensure log file exists
|
||||||
Directory.CreateDirectory(Settings.FileLoggerOptions.LogDirectory);
|
Directory.CreateDirectory(Settings.FileLoggerOptions.LogDirectory);
|
||||||
|
if (LoggerProvider is FileLoggerProvider fileProvider)
|
||||||
|
{
|
||||||
|
fileProvider.InitializeCurrentFile();
|
||||||
|
}
|
||||||
|
|
||||||
_logDate = CurrentDateTime;
|
_logDate = CurrentDateTime;
|
||||||
|
IsRunning = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -291,8 +318,8 @@ namespace EonaCat.Logger.Managers
|
|||||||
_tokenSource?.Dispose();
|
_tokenSource?.Dispose();
|
||||||
_tokenSource = null;
|
_tokenSource = null;
|
||||||
|
|
||||||
LoggerProvider?.Dispose();
|
try { LoggerProvider?.Dispose(); } catch { }
|
||||||
LoggerFactory?.Dispose();
|
try { LoggerFactory?.Dispose(); } catch { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
PatternDetectionInterval = TimeSpan.FromMinutes(3)
|
PatternDetectionInterval = TimeSpan.FromMinutes(3)
|
||||||
};
|
};
|
||||||
|
|
||||||
MemoryGuard.Start(_config);
|
//MemoryGuard.Start(_config);
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
int onLogCounter = 0;
|
int onLogCounter = 0;
|
||||||
@@ -94,6 +94,7 @@
|
|||||||
|
|
||||||
// Create the adapter
|
// Create the adapter
|
||||||
var adapter = new LogCentralEonaCatAdapter(logger.LoggerSettings, logClient);
|
var adapter = new LogCentralEonaCatAdapter(logger.LoggerSettings, logClient);
|
||||||
|
await LogManager.Instance.WriteAsync("LogCentral adapter initialized", ELogType.INFO).ConfigureAwait(false);
|
||||||
|
|
||||||
// Now all EonaCat.Logger logs will be sent to LogCentral automatically
|
// Now all EonaCat.Logger logs will be sent to LogCentral automatically
|
||||||
await logger.LogAsync("This is a test log message sent to LogCentral!", ELogType.INFO).ConfigureAwait(false);
|
await logger.LogAsync("This is a test log message sent to LogCentral!", ELogType.INFO).ConfigureAwait(false);
|
||||||
|
|||||||
Reference in New Issue
Block a user