diff --git a/EonaCat.LogStack/EonaCat.LogStack.csproj b/EonaCat.LogStack/EonaCat.LogStack.csproj
index 3bc7426..a40e77b 100644
--- a/EonaCat.LogStack/EonaCat.LogStack.csproj
+++ b/EonaCat.LogStack/EonaCat.LogStack.csproj
@@ -14,7 +14,7 @@ It features a rich fluent API for routing log events to dozens of destinations f
EonaCat (Jeroen Saey)
EonaCat;Logger;EonaCatLogStack;Log;Writer;Flows;LogStack;Memory;Speed;Jeroen;Saey
- 0.2.1
+ 0.2.2
README.md
True
LICENSE
@@ -25,7 +25,7 @@ It features a rich fluent API for routing log events to dozens of destinations f
- 0.2.1+{chash:10}.{c:ymd}
+ 0.2.2+{chash:10}.{c:ymd}
true
true
v[0-9]*
@@ -36,7 +36,7 @@ It features a rich fluent API for routing log events to dozens of destinations f
- 0.2.1
+ 0.2.2
EonaCat.LogStack
EonaCat.LogStack
https://git.saey.me/EonaCat/EonaCat.LogStack
diff --git a/EonaCat.LogStack/EonaCatLogger.cs b/EonaCat.LogStack/EonaCatLogger.cs
index ce918dd..1dea17a 100644
--- a/EonaCat.LogStack/EonaCatLogger.cs
+++ b/EonaCat.LogStack/EonaCatLogger.cs
@@ -177,6 +177,13 @@ namespace EonaCat.LogStack
private LogLevel EffectiveMinLevel() =>
_dynamicLevel != null ? _dynamicLevel.CurrentLevel : _minimumLevel;
+ ///
+ /// Checks whether an event at the specified level would be accepted.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool IsEnabled(LogLevel level) =>
+ Volatile.Read(ref _disposed) == 0 && level >= EffectiveMinLevel();
+
///
/// Adds a flow (output destination) to this logger
///
@@ -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 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 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);
}
+ ///
+ /// Logs an exception together with structured properties.
+ ///
+ [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));
+ }
+
+ ///
+ /// 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.
+ ///
+ 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
};
diff --git a/EonaCat.LogStack/EonaCatLoggerCore/EonaCatLoggerProvider.cs b/EonaCat.LogStack/EonaCatLoggerCore/EonaCatLoggerProvider.cs
index ef7d2d8..cf5c046 100644
--- a/EonaCat.LogStack/EonaCatLoggerCore/EonaCatLoggerProvider.cs
+++ b/EonaCat.LogStack/EonaCatLoggerCore/EonaCatLoggerProvider.cs
@@ -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(
LogLevel logLevel,
@@ -55,18 +57,55 @@ internal sealed class CategoryLogger : Microsoft.Extensions.Logging.ILogger
}
var message = formatter(state, exception);
-
- _scopes?.ForEachScope