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>
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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