Updated
This commit is contained in:
@@ -14,7 +14,7 @@ It features a rich fluent API for routing log events to dozens of destinations f
|
|||||||
<Copyright>EonaCat (Jeroen Saey)</Copyright>
|
<Copyright>EonaCat (Jeroen Saey)</Copyright>
|
||||||
<PackageTags>EonaCat;Logger;EonaCatLogStack;Log;Writer;Flows;LogStack;Memory;Speed;Jeroen;Saey</PackageTags>
|
<PackageTags>EonaCat;Logger;EonaCatLogStack;Log;Writer;Flows;LogStack;Memory;Speed;Jeroen;Saey</PackageTags>
|
||||||
<PackageIconUrl />
|
<PackageIconUrl />
|
||||||
<FileVersion>0.2.1</FileVersion>
|
<FileVersion>0.2.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 @@ It features a rich fluent API for routing log events to dozens of destinations f
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<EVRevisionFormat>0.2.1+{chash:10}.{c:ymd}</EVRevisionFormat>
|
<EVRevisionFormat>0.2.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>
|
||||||
@@ -36,7 +36,7 @@ It features a rich fluent API for routing log events to dozens of destinations f
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>0.2.1</Version>
|
<Version>0.2.2</Version>
|
||||||
<PackageId>EonaCat.LogStack</PackageId>
|
<PackageId>EonaCat.LogStack</PackageId>
|
||||||
<Product>EonaCat.LogStack</Product>
|
<Product>EonaCat.LogStack</Product>
|
||||||
<RepositoryUrl>https://git.saey.me/EonaCat/EonaCat.LogStack</RepositoryUrl>
|
<RepositoryUrl>https://git.saey.me/EonaCat/EonaCat.LogStack</RepositoryUrl>
|
||||||
|
|||||||
@@ -177,6 +177,13 @@ namespace EonaCat.LogStack
|
|||||||
private LogLevel EffectiveMinLevel() =>
|
private LogLevel EffectiveMinLevel() =>
|
||||||
_dynamicLevel != null ? _dynamicLevel.CurrentLevel : _minimumLevel;
|
_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>
|
/// <summary>
|
||||||
/// Adds a flow (output destination) to this logger
|
/// Adds a flow (output destination) to this logger
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -216,10 +223,10 @@ namespace EonaCat.LogStack
|
|||||||
{
|
{
|
||||||
var keep = _concurrentFlows.Where(f => f.Name != name).ToArray();
|
var keep = _concurrentFlows.Where(f => f.Name != name).ToArray();
|
||||||
while (_concurrentFlows.TryTake(out _)) { }
|
while (_concurrentFlows.TryTake(out _)) { }
|
||||||
foreach (var f in keep)
|
foreach (var f in keep)
|
||||||
{
|
{
|
||||||
_concurrentFlows.Add(f);
|
_concurrentFlows.Add(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -248,26 +255,26 @@ namespace EonaCat.LogStack
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RaiseOnLog(LogMessage message)
|
private void RaiseOnLog(LogMessage message)
|
||||||
{
|
{
|
||||||
var handlers = OnLog;
|
var handlers = OnLog;
|
||||||
|
|
||||||
if (handlers == null)
|
if (handlers == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (EventHandler<LogMessage> handler in handlers.GetInvocationList())
|
foreach (EventHandler<LogMessage> handler in handlers.GetInvocationList())
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
handler(this, message);
|
handler(this, message);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine(ex);
|
System.Diagnostics.Debug.WriteLine(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -357,15 +364,15 @@ namespace EonaCat.LogStack
|
|||||||
.WithException(exception)
|
.WithException(exception)
|
||||||
.WithTimestamp(GetTimestamp());
|
.WithTimestamp(GetTimestamp());
|
||||||
|
|
||||||
ProcessLogEvent(ref builder);
|
ProcessLogEvent(ref builder);
|
||||||
|
|
||||||
RaiseOnLog(new LogMessage
|
RaiseOnLog(new LogMessage
|
||||||
{
|
{
|
||||||
Level = level,
|
Level = level,
|
||||||
Exception = exception,
|
Exception = exception,
|
||||||
Message = message,
|
Message = message,
|
||||||
Category = _category,
|
Category = _category,
|
||||||
Origin = null
|
Origin = null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,14 +425,65 @@ namespace EonaCat.LogStack
|
|||||||
ProcessLogEvent(ref builder);
|
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)
|
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)
|
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);
|
public void Trace(string template, params object[] args) => Write(LogLevel.Trace, template, args);
|
||||||
@@ -608,17 +666,17 @@ namespace EonaCat.LogStack
|
|||||||
.Where(d => d != null)
|
.Where(d => d != null)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
int flowCount;
|
int flowCount;
|
||||||
int boosterCount;
|
int boosterCount;
|
||||||
|
|
||||||
lock (_flows)
|
lock (_flows)
|
||||||
{
|
{
|
||||||
flowCount = _flows.Count;
|
flowCount = _flows.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (_boosters)
|
lock (_boosters)
|
||||||
{
|
{
|
||||||
boosterCount = _boosters.Count;
|
boosterCount = _boosters.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new LoggerDiagnostics
|
return new LoggerDiagnostics
|
||||||
@@ -627,8 +685,8 @@ namespace EonaCat.LogStack
|
|||||||
MinimumLevel = _minimumLevel,
|
MinimumLevel = _minimumLevel,
|
||||||
TotalLogged = Interlocked.Read(ref _totalLoggedCount),
|
TotalLogged = Interlocked.Read(ref _totalLoggedCount),
|
||||||
TotalDropped = Interlocked.Read(ref _totalDroppedCount),
|
TotalDropped = Interlocked.Read(ref _totalDroppedCount),
|
||||||
TotalExceptions = Interlocked.Read(ref _totalExceptionsCount),
|
TotalExceptions = Interlocked.Read(ref _totalExceptionsCount),
|
||||||
FlowCount = flowCount,
|
FlowCount = flowCount,
|
||||||
BoosterCount = boosterCount,
|
BoosterCount = boosterCount,
|
||||||
Flows = flowDiagnostics
|
Flows = flowDiagnostics
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace EonaCat.LogStack.Logging;
|
namespace EonaCat.LogStack.Logging;
|
||||||
|
|
||||||
@@ -40,7 +42,7 @@ internal sealed class CategoryLogger : Microsoft.Extensions.Logging.ILogger
|
|||||||
=> _scopes?.Push(state) ?? NullScope.Instance;
|
=> _scopes?.Push(state) ?? NullScope.Instance;
|
||||||
|
|
||||||
public bool IsEnabled(LogLevel logLevel)
|
public bool IsEnabled(LogLevel logLevel)
|
||||||
=> logLevel != LogLevel.None;
|
=> logLevel != LogLevel.None && _EonaCatLogStack.IsEnabled(logLevel.FromLogLevel());
|
||||||
|
|
||||||
public void Log<TState>(
|
public void Log<TState>(
|
||||||
LogLevel logLevel,
|
LogLevel logLevel,
|
||||||
@@ -55,18 +57,55 @@ internal sealed class CategoryLogger : Microsoft.Extensions.Logging.ILogger
|
|||||||
}
|
}
|
||||||
|
|
||||||
var message = formatter(state, exception);
|
var message = formatter(state, exception);
|
||||||
|
var properties = new List<(string Key, object Value)>(4)
|
||||||
_scopes?.ForEachScope<object?>((scope, _) =>
|
|
||||||
{
|
{
|
||||||
message = $"{message} | Scope={scope}";
|
("Category", _category),
|
||||||
}, null);
|
("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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user