This commit is contained in:
2026-06-16 09:55:05 +02:00
parent aba8b4df46
commit f99e17e838
4 changed files with 1387 additions and 1349 deletions
+3 -3
View File
@@ -14,7 +14,7 @@ It features a rich fluent API for routing log events to dozens of destinations f
<Copyright>EonaCat (Jeroen Saey)</Copyright>
<PackageTags>EonaCat;Logger;EonaCatLogStack;Log;Writer;Flows;LogStack;Memory;Speed;Jeroen;Saey</PackageTags>
<PackageIconUrl />
<FileVersion>0.0.9</FileVersion>
<FileVersion>0.1.0</FileVersion>
<PackageReadmeFile>README.md</PackageReadmeFile>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
@@ -25,7 +25,7 @@ It features a rich fluent API for routing log events to dozens of destinations f
</PropertyGroup>
<PropertyGroup>
<EVRevisionFormat>0.0.9+{chash:10}.{c:ymd}</EVRevisionFormat>
<EVRevisionFormat>0.1.0+{chash:10}.{c:ymd}</EVRevisionFormat>
<EVDefault>true</EVDefault>
<EVInfo>true</EVInfo>
<EVTagMatch>v[0-9]*</EVTagMatch>
@@ -36,7 +36,7 @@ It features a rich fluent API for routing log events to dozens of destinations f
</PropertyGroup>
<PropertyGroup>
<Version>0.0.9</Version>
<Version>0.1.0</Version>
<PackageId>EonaCat.LogStack</PackageId>
<Product>EonaCat.LogStack</Product>
<RepositoryUrl>https://git.saey.me/EonaCat/EonaCat.LogStack</RepositoryUrl>
+71 -41
View File
@@ -30,7 +30,7 @@ namespace EonaCat.LogStack
private readonly LogLevel _minimumLevel;
private readonly TimestampMode _timestampMode;
private volatile bool _isDisposed;
private int _disposed;
private long _totalLoggedCount;
private long _totalDroppedCount;
private long _totalExceptionsCount;
@@ -74,6 +74,9 @@ namespace EonaCat.LogStack
_category = category ?? throw new ArgumentNullException(nameof(category));
_minimumLevel = minimumLevel;
_timestampMode = timestampMode;
// Enable async pipeline by default
UseAsyncPipeline();
}
/// <summary>
@@ -155,6 +158,12 @@ namespace EonaCat.LogStack
public EonaCatLogStack RemoveFlow(string name)
{
lock (_flows) { _flows.RemoveAll(f => f.Name == name); }
lock (_concurrentFlows)
{
var keep = _concurrentFlows.Where(f => f.Name != name).ToArray();
while (_concurrentFlows.TryTake(out _)) { }
foreach (var f in keep) _concurrentFlows.Add(f);
}
return this;
}
@@ -182,10 +191,32 @@ namespace EonaCat.LogStack
return this;
}
private void RaiseOnLog(LogMessage message)
{
var handlers = OnLog;
if (handlers == null)
{
return;
}
foreach (EventHandler<LogMessage> handler in handlers.GetInvocationList())
{
try
{
handler(this, message);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Log(string message, LogLevel level = LogLevel.Information)
{
if (_isDisposed || level < EffectiveMinLevel())
if (Volatile.Read(ref _disposed) == 1 || level < EffectiveMinLevel())
{
return;
}
@@ -207,7 +238,7 @@ namespace EonaCat.LogStack
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void LogTemplate(LogLevel level, string template, params object?[] args)
{
if (_isDisposed || level < EffectiveMinLevel())
if (Volatile.Read(ref _disposed) == 1 || level < EffectiveMinLevel())
{
return;
}
@@ -230,7 +261,7 @@ namespace EonaCat.LogStack
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void LogTemplate(LogLevel level, Exception exception, string template, params object?[] args)
{
if (_isDisposed || level < EffectiveMinLevel())
if (Volatile.Read(ref _disposed) == 1 || level < EffectiveMinLevel())
{
return;
}
@@ -251,7 +282,7 @@ namespace EonaCat.LogStack
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Log(LogLevel level, Exception exception, string message)
{
if (_isDisposed || level < EffectiveMinLevel())
if (Volatile.Read(ref _disposed) == 1 || level < EffectiveMinLevel())
{
return;
}
@@ -271,7 +302,7 @@ namespace EonaCat.LogStack
ProcessLogEvent(ref builder);
OnLog?.Invoke(this, new LogMessage
RaiseOnLog(new LogMessage
{
Level = level,
Exception = exception,
@@ -310,7 +341,7 @@ namespace EonaCat.LogStack
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Log(LogLevel level, string message, params (string Key, object Value)[] properties)
{
if (_isDisposed || level < EffectiveMinLevel())
if (Volatile.Read(ref _disposed) == 1 || level < EffectiveMinLevel())
{
return;
}
@@ -396,7 +427,10 @@ namespace EonaCat.LogStack
}
// Apply modifiers
foreach (var mod in _modifiers)
ActionRef<LogEventBuilder>[] modifierSnapshot;
lock (_modifiersLock) { modifierSnapshot = _modifiers.ToArray(); }
foreach (var mod in modifierSnapshot)
{
try
{
@@ -408,35 +442,10 @@ namespace EonaCat.LogStack
var logEvent = builder.Build();
Interlocked.Increment(ref _totalLoggedCount);
// Async channel pipeline
if (_asyncChannel != null)
{
if (!_asyncChannel.Writer.TryWrite(logEvent))
if (!_asyncChannel!.Writer.TryWrite(logEvent))
{
Interlocked.Increment(ref _totalDroppedCount);
}
return;
}
// Synchronous blast to flows
DispatchToFlows(logEvent);
}
private void DispatchToFlows(LogEvent logEvent)
{
foreach (var flow in _concurrentFlows)
{
try
{
var result = flow.BlastAsync(logEvent).GetAwaiter().GetResult();
if (result == WriteResult.Dropped)
{
Interlocked.Increment(ref _totalDroppedCount);
}
}
catch { }
}
}
private async Task ConsumeChannelAsync(CancellationToken cancellationToken)
@@ -488,6 +497,19 @@ namespace EonaCat.LogStack
.Where(d => d != null)
.ToList();
int flowCount;
int boosterCount;
lock (_flows)
{
flowCount = _flows.Count;
}
lock (_boosters)
{
boosterCount = _boosters.Count;
}
return new LoggerDiagnostics
{
Category = _category,
@@ -495,8 +517,8 @@ namespace EonaCat.LogStack
TotalLogged = Interlocked.Read(ref _totalLoggedCount),
TotalDropped = Interlocked.Read(ref _totalDroppedCount),
TotalExceptions = Interlocked.Read(ref _totalExceptionsCount),
FlowCount = _flows.Count,
BoosterCount = _boosters.Count,
FlowCount = flowCount,
BoosterCount = boosterCount,
Flows = flowDiagnostics
};
}
@@ -535,19 +557,27 @@ namespace EonaCat.LogStack
public async ValueTask DisposeAsync()
{
if (_isDisposed)
if (Interlocked.Exchange(ref _disposed, 1) == 1)
{
return;
}
_isDisposed = true;
// Drain and stop the async channel pipeline if active
if (_asyncChannel != null)
{
_asyncChannel.Writer.TryComplete();
_asyncCts?.Cancel();
try { if (_asyncConsumer != null) { await _asyncConsumer.ConfigureAwait(false); } } catch { }
if (_asyncConsumer != null)
{
try
{
await _asyncConsumer.ConfigureAwait(false);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
}
}
await FlushAsync().ConfigureAwait(false);
@@ -16,6 +16,11 @@ namespace EonaCat.LogStack.Test.Web
{
public static async Task Main(string[] args)
{
await using var logger = LogBuilder.CreateDefault("MyApp");
logger.Information("Application started");
logger.Warning("Low memory warning");
logger.Error(new Exception("DIT IS MIJN TEST!"), "Unexpected error occurred");
// Configure the client
var centralOptions = new LogCentralOptions
@@ -46,6 +51,9 @@ namespace EonaCat.LogStack.Test.Web
var logBuilder = new LogBuilder();
logBuilder.WithTimestampMode(TimestampMode.Local);
logBuilder.WriteToConsole();
logBuilder.WriteToFile("./logs", outputFormat: FileOutputFormat.Csv);
logBuilder.WriteToFile("./logs", outputFormat: FileOutputFormat.StructuredJson);
logBuilder.WriteToFile("./logs", outputFormat: FileOutputFormat.Text);
logBuilder.WriteToFile("./logs", outputFormat: FileOutputFormat.Json);
logBuilder.WriteToFile("./logs", outputFormat: FileOutputFormat.Xml);
logBuilder.WriteToTcp("127.0.0.1", 514);