This commit is contained in:
2026-09-10 08:52:59 +02:00
committed by Jeroen Saey
parent 5c435fb2f5
commit 17d7426b44
3 changed files with 156 additions and 59 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.2.1</FileVersion>
<FileVersion>0.2.2</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.2.1+{chash:10}.{c:ymd}</EVRevisionFormat>
<EVRevisionFormat>0.2.2+{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.2.1</Version>
<Version>0.2.2</Version>
<PackageId>EonaCat.LogStack</PackageId>
<Product>EonaCat.LogStack</Product>
<RepositoryUrl>https://git.saey.me/EonaCat/EonaCat.LogStack</RepositoryUrl>
+106 -48
View File
@@ -177,6 +177,13 @@ namespace EonaCat.LogStack
private LogLevel EffectiveMinLevel() =>
_dynamicLevel != null ? _dynamicLevel.CurrentLevel : _minimumLevel;
/// <summary>
/// Checks whether an event at the specified level would be accepted.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsEnabled(LogLevel level) =>
Volatile.Read(ref _disposed) == 0 && level >= EffectiveMinLevel();
/// <summary>
/// Adds a flow (output destination) to this logger
/// </summary>
@@ -216,10 +223,10 @@ namespace EonaCat.LogStack
{
var keep = _concurrentFlows.Where(f => f.Name != name).ToArray();
while (_concurrentFlows.TryTake(out _)) { }
foreach (var f in keep)
{
_concurrentFlows.Add(f);
}
foreach (var f in keep)
{
_concurrentFlows.Add(f);
}
}
return this;
}
@@ -248,26 +255,26 @@ 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);
}
}
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)]
@@ -357,15 +364,15 @@ namespace EonaCat.LogStack
.WithException(exception)
.WithTimestamp(GetTimestamp());
ProcessLogEvent(ref builder);
RaiseOnLog(new LogMessage
{
Level = level,
Exception = exception,
Message = message,
Category = _category,
Origin = null
ProcessLogEvent(ref builder);
RaiseOnLog(new LogMessage
{
Level = level,
Exception = exception,
Message = message,
Category = _category,
Origin = null
});
}
@@ -418,14 +425,65 @@ namespace EonaCat.LogStack
ProcessLogEvent(ref builder);
}
/// <summary>
/// Logs an exception together with structured properties.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Log(LogLevel level, Exception exception, string message, params (string Key, object Value)[] properties)
{
if (Volatile.Read(ref _disposed) == 1 || level < EffectiveMinLevel())
{
return;
}
TrackLevel(level);
Interlocked.Increment(ref _totalExceptionsCount);
var builder = new LogEventBuilder()
.WithLevel(level)
.WithCategory(_category)
.WithMessage(message)
.WithException(exception)
.WithTimestamp(GetTimestamp());
foreach (var (key, value) in properties)
{
builder.WithProperty(key, value);
}
ProcessLogEvent(ref builder);
}
private void Write(LogLevel level, string template, params object[] args)
{
Log(level, string.Format(template, args));
Log(level, FormatTemplate(template, args));
}
private void Write(LogLevel level, Exception ex, string template, params object[] args)
{
Log(level, ex, string.Format(template, args));
Log(level, ex, FormatTemplate(template, args));
}
/// <summary>
/// Formats a message template, supporting both Serilog-style named holes (e.g. "{UserId}")
/// and classic string.Format-style positional holes (e.g. "{0}"). Never throws on malformed
/// templates; falls back to the raw template text instead.
/// </summary>
private static string FormatTemplate(string template, object[] args)
{
if (string.IsNullOrEmpty(template))
{
return template;
}
try
{
return MessageTemplate.FromCache(template).Render(args, out _);
}
catch
{
return template;
}
}
public void Trace(string template, params object[] args) => Write(LogLevel.Trace, template, args);
@@ -608,17 +666,17 @@ namespace EonaCat.LogStack
.Where(d => d != null)
.ToList();
int flowCount;
int boosterCount;
lock (_flows)
{
flowCount = _flows.Count;
}
lock (_boosters)
{
boosterCount = _boosters.Count;
int flowCount;
int boosterCount;
lock (_flows)
{
flowCount = _flows.Count;
}
lock (_boosters)
{
boosterCount = _boosters.Count;
}
return new LoggerDiagnostics
@@ -627,8 +685,8 @@ namespace EonaCat.LogStack
MinimumLevel = _minimumLevel,
TotalLogged = Interlocked.Read(ref _totalLoggedCount),
TotalDropped = Interlocked.Read(ref _totalDroppedCount),
TotalExceptions = Interlocked.Read(ref _totalExceptionsCount),
FlowCount = flowCount,
TotalExceptions = Interlocked.Read(ref _totalExceptionsCount),
FlowCount = flowCount,
BoosterCount = boosterCount,
Flows = flowDiagnostics
};
@@ -1,5 +1,7 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
namespace EonaCat.LogStack.Logging;
@@ -40,7 +42,7 @@ internal sealed class CategoryLogger : Microsoft.Extensions.Logging.ILogger
=> _scopes?.Push(state) ?? NullScope.Instance;
public bool IsEnabled(LogLevel logLevel)
=> logLevel != LogLevel.None;
=> logLevel != LogLevel.None && _EonaCatLogStack.IsEnabled(logLevel.FromLogLevel());
public void Log<TState>(
LogLevel logLevel,
@@ -55,18 +57,55 @@ internal sealed class CategoryLogger : Microsoft.Extensions.Logging.ILogger
}
var message = formatter(state, exception);
_scopes?.ForEachScope<object?>((scope, _) =>
var properties = new List<(string Key, object Value)>(4)
{
message = $"{message} | Scope={scope}";
}, null);
("Category", _category),
("EventId", eventId.Id)
};
if (exception != null)
if (!string.IsNullOrEmpty(eventId.Name))
{
message = $"{message} | Exception={exception}";
properties.Add(("EventName", eventId.Name));
}
_EonaCatLogStack.Log($"[{logLevel}] [{_category}] {message}");
if (state is IEnumerable<KeyValuePair<string, object?>> structuredState)
{
foreach (var pair in structuredState)
{
if (!string.IsNullOrEmpty(pair.Key) && pair.Value != null)
{
properties.Add((pair.Key, pair.Value));
}
}
}
_scopes?.ForEachScope<List<(string Key, object Value)>>((scope, stateProperties) =>
{
if (scope is IEnumerable<KeyValuePair<string, object?>> structuredScope)
{
foreach (var pair in structuredScope)
{
if (!string.IsNullOrEmpty(pair.Key) && pair.Value != null)
{
stateProperties.Add((pair.Key, pair.Value));
}
}
}
else if (scope != null)
{
stateProperties.Add(("Scope", scope));
}
}, properties);
var level = logLevel.FromLogLevel();
if (exception == null)
{
_EonaCatLogStack.Log(level, message, properties.ToArray());
}
else
{
_EonaCatLogStack.Log(level, exception, message, properties.ToArray());
}
}
}