Added dependency injection
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>
|
||||
<PackageTags>EonaCat;Logger;EonaCatLogStack;Log;Writer;Flows;LogStack;Memory;Speed;Jeroen;Saey</PackageTags>
|
||||
<PackageIconUrl />
|
||||
<FileVersion>0.0.6</FileVersion>
|
||||
<FileVersion>0.0.7</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.6+{chash:10}.{c:ymd}</EVRevisionFormat>
|
||||
<EVRevisionFormat>0.0.7+{chash:10}.{c:ymd}</EVRevisionFormat>
|
||||
<EVDefault>true</EVDefault>
|
||||
<EVInfo>true</EVInfo>
|
||||
<EVTagMatch>v[0-9]*</EVTagMatch>
|
||||
|
||||
@@ -178,15 +178,51 @@ namespace EonaCat.LogStack
|
||||
ProcessLogEvent(ref builder);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] public void Trace(string message) => Log(LogLevel.Trace, message);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] public void Debug(string message) => Log(LogLevel.Debug, message);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] public void Information(string message) => Log(LogLevel.Information, message);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] public void Warning(string message) => Log(LogLevel.Warning, message);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] public void Warning(Exception ex, string message) => Log(LogLevel.Warning, ex, message);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] public void Error(string message) => Log(LogLevel.Error, message);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] public void Error(Exception ex, string message) => Log(LogLevel.Error, ex, message);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] public void Critical(string message) => Log(LogLevel.Critical, message);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] public void Critical(Exception ex, string message) => Log(LogLevel.Critical, ex, message);
|
||||
private void Write(LogLevel level, string template, params object[] args)
|
||||
{
|
||||
Log(level, string.Format(template, args));
|
||||
}
|
||||
|
||||
private void Write(LogLevel level, Exception ex, string template, params object[] args)
|
||||
{
|
||||
Log(level, ex, string.Format(template, args));
|
||||
}
|
||||
|
||||
public void Trace(string template, params object[] args) => Write(LogLevel.Trace, template, args);
|
||||
|
||||
public void Debug(string template, params object[] args) => Write(LogLevel.Debug, template, args);
|
||||
|
||||
public void Information(string template, params object[] args) => Write(LogLevel.Information, template, args);
|
||||
|
||||
public void Warning(string template, params object[] args) => Write(LogLevel.Warning, template, args);
|
||||
|
||||
public void Warning(Exception ex, string template, params object[] args) => Write(LogLevel.Warning, ex, template, args);
|
||||
|
||||
public void Error(string template, params object[] args) => Write(LogLevel.Error, template, args);
|
||||
|
||||
public void Error(Exception ex, string template, params object[] args) => Write(LogLevel.Error, ex, template, args);
|
||||
|
||||
public void Critical(string template, params object[] args) => Write(LogLevel.Critical, template, args);
|
||||
|
||||
public void Critical(Exception ex, string template, params object[] args) => Write(LogLevel.Critical, ex, template, args);
|
||||
|
||||
public void LogTrace(string template, params object[] args) => Write(LogLevel.Trace, template, args);
|
||||
|
||||
public void LogDebug(string template, params object[] args) => Write(LogLevel.Debug, template, args);
|
||||
|
||||
public void LogInformation(string template, params object[] args) => Write(LogLevel.Information, template, args);
|
||||
|
||||
public void LogWarning(string template, params object[] args) => Write(LogLevel.Warning, template, args);
|
||||
|
||||
public void LogWarning(Exception ex, string template, params object[] args) => Write(LogLevel.Warning, ex, template, args);
|
||||
|
||||
public void LogError(string template, params object[] args) => Write(LogLevel.Error, template, args);
|
||||
|
||||
public void LogError(Exception ex, string template, params object[] args) => Write(LogLevel.Error, ex, template, args);
|
||||
|
||||
public void LogCritical(string template, params object[] args) => Write(LogLevel.Critical, template, args);
|
||||
|
||||
public void LogCritical(Exception ex, string template, params object[] args) => Write(LogLevel.Critical, ex, template, args);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ProcessLogEvent(ref LogEventBuilder builder)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
using EonaCat.LogStack.Core;
|
||||
using System;
|
||||
|
||||
namespace EonaCat.LogStack.Logging;
|
||||
|
||||
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
||||
|
||||
/// <summary>
|
||||
/// Adapter interface that wraps EonaCatLogStack to be compatible with Microsoft.Extensions.Logging.ILogger pattern
|
||||
/// </summary>
|
||||
public interface ILogger
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the category/name of this logger
|
||||
/// </summary>
|
||||
string Category { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Logs a message at the specified log level
|
||||
/// </summary>
|
||||
void Log(LogLevel level, string message);
|
||||
|
||||
/// <summary>
|
||||
/// Logs a message with an exception at the specified log level
|
||||
/// </summary>
|
||||
void Log(LogLevel level, Exception exception, string message);
|
||||
|
||||
/// <summary>
|
||||
/// Logs a formatted message at the specified log level
|
||||
/// </summary>
|
||||
void Log(LogLevel level, string format, params object[] args);
|
||||
|
||||
/// <summary>
|
||||
/// Logs a formatted message with an exception at the specified log level
|
||||
/// </summary>
|
||||
void Log(LogLevel level, Exception exception, string format, params object[] args);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given log level is enabled for this logger
|
||||
/// </summary>
|
||||
bool IsEnabled(LogLevel level);
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
using EonaCat.LogStack.Core;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace EonaCat.LogStack.Logging;
|
||||
|
||||
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
||||
|
||||
/// <summary>
|
||||
/// Factory for creating logger instances with a shared configuration
|
||||
/// </summary>
|
||||
public interface ILoggerFactory : IAsyncDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates or retrieves a logger for the specified category
|
||||
/// </summary>
|
||||
ILogger CreateLogger(string categoryName);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying EonaCatLogStack instance
|
||||
/// </summary>
|
||||
EonaCatLogStack GetLogStack();
|
||||
|
||||
/// <summary>
|
||||
/// Flushes all pending log events
|
||||
/// </summary>
|
||||
System.Threading.Tasks.Task FlushAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Gets diagnostics information about the logger
|
||||
/// </summary>
|
||||
LoggerDiagnostics GetDiagnostics();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of ILoggerFactory that manages a single EonaCatLogStack instance
|
||||
/// </summary>
|
||||
public sealed class LoggerFactory : ILoggerFactory
|
||||
{
|
||||
private readonly EonaCatLogStack _logStack;
|
||||
private readonly ConcurrentDictionary<string, Logger> _loggers;
|
||||
private volatile bool _isDisposed;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new LoggerFactory with default settings
|
||||
/// </summary>
|
||||
public LoggerFactory(
|
||||
LogLevel minimumLevel = LogLevel.Trace,
|
||||
TimestampMode timestampMode = TimestampMode.Utc)
|
||||
{
|
||||
_logStack = new EonaCatLogStack(
|
||||
category: "LoggerFactory",
|
||||
minimumLevel: minimumLevel,
|
||||
timestampMode: timestampMode);
|
||||
_loggers = new ConcurrentDictionary<string, Logger>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new LoggerFactory using an existing EonaCatLogStack instance
|
||||
/// </summary>
|
||||
public LoggerFactory(EonaCatLogStack logStack)
|
||||
{
|
||||
_logStack = logStack ?? throw new ArgumentNullException(nameof(logStack));
|
||||
_loggers = new ConcurrentDictionary<string, Logger>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates or retrieves a logger for the specified category
|
||||
/// </summary>
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(LoggerFactory));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(categoryName))
|
||||
{
|
||||
categoryName = "Default";
|
||||
}
|
||||
|
||||
return _loggers.GetOrAdd(categoryName, name => new Logger(name, _logStack));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying EonaCatLogStack instance
|
||||
/// </summary>
|
||||
public EonaCatLogStack GetLogStack()
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(LoggerFactory));
|
||||
}
|
||||
|
||||
return _logStack;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes all pending log events
|
||||
/// </summary>
|
||||
public async System.Threading.Tasks.Task FlushAsync()
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(LoggerFactory));
|
||||
}
|
||||
|
||||
await _logStack.FlushAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets diagnostics information about the logger
|
||||
/// </summary>
|
||||
public LoggerDiagnostics GetDiagnostics()
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(LoggerFactory));
|
||||
}
|
||||
|
||||
return _logStack.GetDiagnostics();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes all loggers and the underlying log stack
|
||||
/// </summary>
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDisposed = true;
|
||||
_loggers.Clear();
|
||||
await _logStack.DisposeAsync().ConfigureAwait(false);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal logger adapter that wraps EonaCatLogStack
|
||||
/// </summary>
|
||||
private sealed class Logger : ILogger
|
||||
{
|
||||
private readonly string _category;
|
||||
private readonly EonaCatLogStack _logStack;
|
||||
|
||||
public string Category => _category;
|
||||
|
||||
public Logger(string category, EonaCatLogStack logStack)
|
||||
{
|
||||
_category = category ?? "Default";
|
||||
_logStack = logStack;
|
||||
}
|
||||
|
||||
public void Log(LogLevel level, string message)
|
||||
{
|
||||
if (!IsEnabled(level))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_logStack.Log(message, level);
|
||||
}
|
||||
|
||||
public void Log(LogLevel level, Exception exception, string message)
|
||||
{
|
||||
if (!IsEnabled(level))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_logStack.Log(level, exception, message);
|
||||
}
|
||||
|
||||
public void Log(LogLevel level, string format, params object[] args)
|
||||
{
|
||||
if (!IsEnabled(level))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var message = string.Format(format, args);
|
||||
_logStack.Log(message, level);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Fallback if formatting fails
|
||||
_logStack.Log(format, level);
|
||||
}
|
||||
}
|
||||
|
||||
public void Log(LogLevel level, Exception exception, string format, params object[] args)
|
||||
{
|
||||
if (!IsEnabled(level))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var message = string.Format(format, args);
|
||||
_logStack.Log(level, exception, message);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Fallback if formatting fails
|
||||
_logStack.Log(level, exception, format);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel level)
|
||||
{
|
||||
// Check if level is enabled in the logstack
|
||||
// This is a simple check; could be enhanced
|
||||
return level >= LogLevel.Trace;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
using EonaCat.LogStack.Core;
|
||||
using System;
|
||||
|
||||
namespace EonaCat.LogStack.Logging;
|
||||
|
||||
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
||||
|
||||
/// <summary>
|
||||
/// Adapter that bridges EonaCat logging with Microsoft.Extensions.Logging
|
||||
/// This allows EonaCatLogStack to be used where Microsoft.Extensions.Logging.ILogger is expected
|
||||
/// </summary>
|
||||
public sealed class MicrosoftExtensionsLoggerAdapter : Microsoft.Extensions.Logging.ILogger
|
||||
{
|
||||
private readonly ILogger _eonaCatLogger;
|
||||
|
||||
public MicrosoftExtensionsLoggerAdapter(ILogger eonaCatLogger)
|
||||
{
|
||||
_eonaCatLogger = eonaCatLogger ?? throw new ArgumentNullException(nameof(eonaCatLogger));
|
||||
}
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
// EonaCat doesn't support scopes out of the box, but we can return a no-op disposable
|
||||
return new NoOpDisposable();
|
||||
}
|
||||
|
||||
public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel)
|
||||
{
|
||||
var eonaCatLevel = ConvertLogLevel(logLevel);
|
||||
return _eonaCatLogger.IsEnabled(eonaCatLevel);
|
||||
}
|
||||
|
||||
public void Log<TState>(
|
||||
Microsoft.Extensions.Logging.LogLevel logLevel,
|
||||
Microsoft.Extensions.Logging.EventId eventId,
|
||||
TState state,
|
||||
Exception exception,
|
||||
Func<TState, Exception, string> formatter)
|
||||
{
|
||||
if (!IsEnabled(logLevel))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var eonaCatLevel = ConvertLogLevel(logLevel);
|
||||
var message = formatter(state, exception);
|
||||
|
||||
if (exception != null)
|
||||
{
|
||||
_eonaCatLogger.Log(eonaCatLevel, exception, message);
|
||||
}
|
||||
else
|
||||
{
|
||||
_eonaCatLogger.Log(eonaCatLevel, message);
|
||||
}
|
||||
}
|
||||
|
||||
private static LogLevel ConvertLogLevel(Microsoft.Extensions.Logging.LogLevel msLevel)
|
||||
{
|
||||
return msLevel switch
|
||||
{
|
||||
Microsoft.Extensions.Logging.LogLevel.Trace => LogLevel.Trace,
|
||||
Microsoft.Extensions.Logging.LogLevel.Debug => LogLevel.Debug,
|
||||
Microsoft.Extensions.Logging.LogLevel.Information => LogLevel.Information,
|
||||
Microsoft.Extensions.Logging.LogLevel.Warning => LogLevel.Warning,
|
||||
Microsoft.Extensions.Logging.LogLevel.Error => LogLevel.Error,
|
||||
Microsoft.Extensions.Logging.LogLevel.Critical => LogLevel.Critical,
|
||||
_ => LogLevel.Information
|
||||
};
|
||||
}
|
||||
|
||||
private sealed class NoOpDisposable : IDisposable
|
||||
{
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory adapter that implements Microsoft.Extensions.Logging.ILoggerFactory
|
||||
/// </summary>
|
||||
public sealed class MicrosoftExtensionsLoggerFactoryAdapter : Microsoft.Extensions.Logging.ILoggerFactory
|
||||
{
|
||||
private readonly ILoggerFactory _eonaCatFactory;
|
||||
private volatile bool _isDisposed;
|
||||
|
||||
public MicrosoftExtensionsLoggerFactoryAdapter(ILoggerFactory eonaCatFactory)
|
||||
{
|
||||
_eonaCatFactory = eonaCatFactory ?? throw new ArgumentNullException(nameof(eonaCatFactory));
|
||||
}
|
||||
|
||||
public Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(MicrosoftExtensionsLoggerFactoryAdapter));
|
||||
}
|
||||
|
||||
var eonaCatLogger = _eonaCatFactory.CreateLogger(categoryName);
|
||||
return new MicrosoftExtensionsLoggerAdapter(eonaCatLogger);
|
||||
}
|
||||
|
||||
public void AddProvider(Microsoft.Extensions.Logging.ILoggerProvider provider)
|
||||
{
|
||||
// EonaCat uses Flows instead of providers, so this is a no-op
|
||||
// Providers should be configured through the ILoggerFactory.GetLogStack()
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDisposed = true;
|
||||
_eonaCatFactory.DisposeAsync().GetAwaiter().GetResult();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
using EonaCat.LogStack.Configuration;
|
||||
using EonaCat.LogStack.Core;
|
||||
using EonaCat.LogStack.Logging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
|
||||
namespace EonaCat.LogStack.Extensions;
|
||||
|
||||
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
||||
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for integrating EonaCat LogStack with Microsoft Dependency Injection
|
||||
/// </summary>
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers EonaCat LogStack as the logging provider in the dependency injection container
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection to register with</param>
|
||||
/// <param name="minimumLevel">The minimum log level to process</param>
|
||||
/// <param name="timestampMode">The timestamp mode to use</param>
|
||||
/// <returns>The service collection for chaining</returns>
|
||||
public static IServiceCollection AddEonaCatLogging(
|
||||
this IServiceCollection services,
|
||||
LogLevel minimumLevel = LogLevel.Trace,
|
||||
TimestampMode timestampMode = TimestampMode.Utc)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
var loggerFactory = new LoggerFactory(minimumLevel, timestampMode);
|
||||
services.AddSingleton<ILoggerFactory>(loggerFactory);
|
||||
services.AddSingleton<Microsoft.Extensions.Logging.ILoggerFactory>(
|
||||
new MicrosoftExtensionsLoggerFactoryAdapter(loggerFactory));
|
||||
|
||||
// Also register as ILogger for constructor injection
|
||||
services.AddSingleton(sp => sp.GetRequiredService<ILoggerFactory>().CreateLogger("Default"));
|
||||
services.AddSingleton(sp =>
|
||||
new MicrosoftExtensionsLoggerAdapter(
|
||||
sp.GetRequiredService<ILoggerFactory>().CreateLogger("Default")));
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers EonaCat LogStack using an existing EonaCatLogStack instance
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection to register with</param>
|
||||
/// <param name="logStack">The pre-configured EonaCatLogStack instance</param>
|
||||
/// <returns>The service collection for chaining</returns>
|
||||
public static IServiceCollection AddEonaCatLogging(
|
||||
this IServiceCollection services,
|
||||
EonaCatLogStack logStack)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
if (logStack == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logStack));
|
||||
}
|
||||
|
||||
var loggerFactory = new LoggerFactory(logStack);
|
||||
services.AddSingleton<ILoggerFactory>(loggerFactory);
|
||||
services.AddSingleton<Microsoft.Extensions.Logging.ILoggerFactory>(
|
||||
new MicrosoftExtensionsLoggerFactoryAdapter(loggerFactory));
|
||||
|
||||
// Also register as ILogger for constructor injection
|
||||
services.AddSingleton(sp => sp.GetRequiredService<ILoggerFactory>().CreateLogger("Default"));
|
||||
services.AddSingleton(sp =>
|
||||
new MicrosoftExtensionsLoggerAdapter(
|
||||
sp.GetRequiredService<ILoggerFactory>().CreateLogger("Default")));
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers EonaCat LogStack with a configuration callback
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection to register with</param>
|
||||
/// <param name="configure">Callback to configure the EonaCatLogStack instance</param>
|
||||
/// <returns>The service collection for chaining</returns>
|
||||
public static IServiceCollection AddEonaCatLogging(
|
||||
this IServiceCollection services,
|
||||
Action<EonaCatLogStack> configure)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
var logStack = new EonaCatLogStack();
|
||||
configure(logStack);
|
||||
|
||||
var loggerFactory = new LoggerFactory(logStack);
|
||||
services.AddSingleton<ILoggerFactory>(loggerFactory);
|
||||
services.AddSingleton<Microsoft.Extensions.Logging.ILoggerFactory>(
|
||||
new MicrosoftExtensionsLoggerFactoryAdapter(loggerFactory));
|
||||
|
||||
// Also register as ILogger for constructor injection
|
||||
services.AddSingleton(sp => sp.GetRequiredService<ILoggerFactory>().CreateLogger("Default"));
|
||||
services.AddSingleton(sp =>
|
||||
new MicrosoftExtensionsLoggerAdapter(
|
||||
sp.GetRequiredService<ILoggerFactory>().CreateLogger("Default")));
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers EonaCat LogStack using a fluent LogBuilder configuration.
|
||||
/// This is the recommended approach for DI integration with fluent configuration.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection to register with</param>
|
||||
/// <param name="category">The category/name of the logger</param>
|
||||
/// <param name="configure">Callback to configure the LogBuilder instance</param>
|
||||
/// <returns>The service collection for chaining</returns>
|
||||
public static IServiceCollection AddEonaCatLogging(
|
||||
this IServiceCollection services,
|
||||
string category,
|
||||
Action<LogBuilder> configure)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
var builder = new LogBuilder(category);
|
||||
configure(builder);
|
||||
var logStack = builder.Build();
|
||||
|
||||
var loggerFactory = new LoggerFactory(logStack);
|
||||
services.AddSingleton<ILoggerFactory>(loggerFactory);
|
||||
services.AddSingleton<Microsoft.Extensions.Logging.ILoggerFactory>(
|
||||
new MicrosoftExtensionsLoggerFactoryAdapter(loggerFactory));
|
||||
|
||||
// Also register as ILogger for constructor injection
|
||||
services.AddSingleton(sp => sp.GetRequiredService<ILoggerFactory>().CreateLogger(category));
|
||||
services.AddSingleton(sp =>
|
||||
new MicrosoftExtensionsLoggerAdapter(
|
||||
sp.GetRequiredService<ILoggerFactory>().CreateLogger(category)));
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers EonaCat LogStack using a fluent LogBuilder configuration with "Application" as the default category.
|
||||
/// This is the recommended approach for DI integration with fluent configuration.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection to register with</param>
|
||||
/// <param name="configure">Callback to configure the LogBuilder instance</param>
|
||||
/// <returns>The service collection for chaining</returns>
|
||||
public static IServiceCollection AddEonaCatLogging(
|
||||
this IServiceCollection services,
|
||||
Action<LogBuilder> configure)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
return services.AddEonaCatLogging("Application", configure);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers EonaCat LogStack as a factory using a LogBuilder configuration callback.
|
||||
/// Allows configuring the logger factory through DI with full access to the service provider.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection to register with</param>
|
||||
/// <param name="category">The category/name of the logger</param>
|
||||
/// <param name="configure">Callback to configure the LogBuilder instance</param>
|
||||
/// <returns>The service collection for chaining</returns>
|
||||
public static IServiceCollection AddEonaCatLoggingFactory(
|
||||
this IServiceCollection services,
|
||||
string category,
|
||||
Action<LogBuilder> configure)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
services.AddSingleton<ILoggerFactory>(sp =>
|
||||
{
|
||||
var builder = new LogBuilder(category);
|
||||
configure(builder);
|
||||
var logStack = builder.Build();
|
||||
return new LoggerFactory(logStack);
|
||||
});
|
||||
|
||||
services.AddSingleton<Microsoft.Extensions.Logging.ILoggerFactory>(sp =>
|
||||
new MicrosoftExtensionsLoggerFactoryAdapter(
|
||||
sp.GetRequiredService<ILoggerFactory>()));
|
||||
|
||||
// Also register as ILogger for constructor injection
|
||||
services.AddSingleton(sp => sp.GetRequiredService<ILoggerFactory>().CreateLogger(category));
|
||||
services.AddSingleton(sp =>
|
||||
new MicrosoftExtensionsLoggerAdapter(
|
||||
sp.GetRequiredService<ILoggerFactory>().CreateLogger(category)));
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers EonaCat LogStack as a factory using a LogBuilder configuration callback with "Application" as default category.
|
||||
/// Allows configuring the logger factory through DI with full access to the service provider.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection to register with</param>
|
||||
/// <param name="configure">Callback to configure the LogBuilder instance</param>
|
||||
/// <returns>The service collection for chaining</returns>
|
||||
public static IServiceCollection AddEonaCatLoggingFactory(
|
||||
this IServiceCollection services,
|
||||
Action<LogBuilder> configure)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
return services.AddEonaCatLoggingFactory("Application", configure);
|
||||
}
|
||||
}
|
||||
@@ -475,3 +475,337 @@ Console.CancelKeyPress += (_, e) => { e.Cancel = true; server.Stop(); };
|
||||
```
|
||||
|
||||
`Stop()` prints a throughput summary and disposes all listeners cleanly.
|
||||
|
||||
|
||||
## Dependency Injection (DI) registration methods
|
||||
|
||||
### 1. Basic Registration (Simplest)
|
||||
|
||||
Register with default settings:
|
||||
|
||||
```csharp
|
||||
services.AddEonaCatLogging();
|
||||
```
|
||||
|
||||
This registers:
|
||||
- `ILoggerFactory` - For creating category-specific loggers
|
||||
- `Microsoft.Extensions.Logging.ILoggerFactory` - For Microsoft.Extensions.Logging compatibility
|
||||
- `ILogger` - For injecting the default logger
|
||||
|
||||
### 2. Registration with Log Level and Timestamp Mode
|
||||
|
||||
```csharp
|
||||
services.AddEonaCatLogging(
|
||||
minimumLevel: LogLevel.Information,
|
||||
timestampMode: TimestampMode.Local);
|
||||
```
|
||||
|
||||
### 3. Registration with Pre-built EonaCatLogStack
|
||||
|
||||
If you've already created an `EonaCatLogStack` instance:
|
||||
|
||||
```csharp
|
||||
var logStack = new EonaCatLogStack("MyApp");
|
||||
logStack.AddFlow(new ConsoleFlow());
|
||||
|
||||
services.AddEonaCatLogging(logStack);
|
||||
```
|
||||
|
||||
### 4. Registration with Configuration Callback (Recommended)
|
||||
|
||||
Configure the logger directly with an `Action<EonaCatLogStack>`:
|
||||
|
||||
```csharp
|
||||
services.AddEonaCatLogging(logStack =>
|
||||
{
|
||||
logStack.AddFlow(new ConsoleFlow());
|
||||
logStack.AddFlow(new FileFlow("./logs"));
|
||||
logStack.AddBooster(new MachineNameBooster());
|
||||
});
|
||||
```
|
||||
|
||||
### 5. Registration with LogBuilder (Most Fluent - Recommended)
|
||||
|
||||
Use the fluent `LogBuilder` API for the most intuitive configuration:
|
||||
|
||||
```csharp
|
||||
services.AddEonaCatLogging("MyApplication", builder =>
|
||||
{
|
||||
builder
|
||||
.WithMinimumLevel(LogLevel.Information)
|
||||
.WithTimestampMode(TimestampMode.Local)
|
||||
.WriteToConsole()
|
||||
.WriteToFile("./logs")
|
||||
.BoostWithMachineName()
|
||||
.BoostWithProcessId()
|
||||
.BoostWithCorrelationId();
|
||||
});
|
||||
```
|
||||
|
||||
Or with the default "Application" category:
|
||||
|
||||
```csharp
|
||||
services.AddEonaCatLogging(builder =>
|
||||
{
|
||||
builder
|
||||
.WriteToConsole()
|
||||
.WriteToFile("./logs");
|
||||
});
|
||||
```
|
||||
|
||||
### 6. Registration with Factory Method (Advanced)
|
||||
|
||||
For advanced scenarios where you need access to the service provider:
|
||||
|
||||
```csharp
|
||||
services.AddEonaCatLoggingFactory("MyApplication", builder =>
|
||||
{
|
||||
builder
|
||||
.WriteToConsole()
|
||||
.WriteToFile("./logs");
|
||||
});
|
||||
```
|
||||
|
||||
## Using in Your Application
|
||||
|
||||
### Injecting ILoggerFactory
|
||||
|
||||
```csharp
|
||||
public class MyService
|
||||
{
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
|
||||
public MyService(ILoggerFactory loggerFactory)
|
||||
{
|
||||
_loggerFactory = loggerFactory;
|
||||
}
|
||||
|
||||
public void DoSomething()
|
||||
{
|
||||
var logger = _loggerFactory.CreateLogger("MyService");
|
||||
logger.Log(LogLevel.Information, "Doing something");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Injecting ILogger
|
||||
|
||||
```csharp
|
||||
public class MyService
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public MyService(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void DoSomething()
|
||||
{
|
||||
_logger.Log(LogLevel.Information, "Doing something");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Using with Microsoft.Extensions.Logging.ILogger
|
||||
|
||||
```csharp
|
||||
public class MyService
|
||||
{
|
||||
private readonly Microsoft.Extensions.Logging.ILogger _logger;
|
||||
|
||||
public MyService(Microsoft.Extensions.Logging.ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void DoSomething()
|
||||
{
|
||||
_logger.LogInformation("Doing something");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ASP.NET Core / Razor Pages Integration
|
||||
|
||||
In your `Program.cs`:
|
||||
|
||||
```csharp
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add EonaCat LogStack to the service collection
|
||||
builder.Services.AddEonaCatLogging("WebApplication", logBuilder =>
|
||||
{
|
||||
logBuilder
|
||||
.WithMinimumLevel(LogLevel.Information)
|
||||
.WriteToConsole(useColors: true)
|
||||
.WriteToFile("./logs")
|
||||
.BoostWithCorrelationId()
|
||||
.BoostWithThreadId();
|
||||
});
|
||||
|
||||
// Rest of your configuration...
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure HTTP request pipeline...
|
||||
app.Run();
|
||||
```
|
||||
|
||||
### In Razor Page Code-Behind
|
||||
|
||||
```csharp
|
||||
public class IndexModel : PageModel
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public IndexModel(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
_logger.Log(LogLevel.Information, "Index page loaded");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
### Multiple Output Destinations (Flows)
|
||||
|
||||
```csharp
|
||||
services.AddEonaCatLogging(builder =>
|
||||
{
|
||||
builder
|
||||
.WriteToConsole() // Console output
|
||||
.WriteToFile("./logs") // File output
|
||||
.WriteToSlack("https://hooks.slack.com/...") // Slack
|
||||
.WriteToDiscord("https://discordapp.com/...") // Discord
|
||||
.WriteToElasticSearch("http://localhost:9200") // Elasticsearch
|
||||
.WriteToEmail("smtp.gmail.com", 587, ...) // Email
|
||||
.WriteToMicrosoftTeams("https://..."); // Teams
|
||||
});
|
||||
```
|
||||
|
||||
### Enriching Log Events (Boosters)
|
||||
|
||||
```csharp
|
||||
services.AddEonaCatLogging(builder =>
|
||||
{
|
||||
builder
|
||||
.BoostWithMachineName() // Adds machine name
|
||||
.BoostWithProcessId() // Adds process ID
|
||||
.BoostWithThreadId() // Adds thread ID
|
||||
.BoostWithCorrelationId() // Adds correlation ID (for distributed tracing)
|
||||
.BoostWithMemory() // Adds memory usage
|
||||
.BoostWithOS() // Adds OS info
|
||||
.BoostWithUser() // Adds username
|
||||
.BoostWithCustomText("Environment", "Production"); // Custom properties
|
||||
});
|
||||
```
|
||||
|
||||
### Log Level Filtering
|
||||
|
||||
```csharp
|
||||
services.AddEonaCatLogging(builder =>
|
||||
{
|
||||
builder
|
||||
.WithMinimumLevel(LogLevel.Warning) // Only log warnings and above
|
||||
.WriteToConsole(minimumLevel: LogLevel.Information) // More verbose for console
|
||||
.WriteToFile("./logs", minimumLevel: LogLevel.Error); // Only errors to file
|
||||
});
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use LogBuilder for Configuration**: The fluent LogBuilder API is the most readable and maintainable approach.
|
||||
|
||||
2. **Register Early**: Register logging in `Program.cs` before other services that depend on logging.
|
||||
|
||||
3. **Use Appropriate Log Levels**:
|
||||
- `Trace` - Very detailed diagnostic info
|
||||
- `Debug` - Debug-level diagnostic info
|
||||
- `Information` - General informational messages
|
||||
- `Warning` - Warning messages
|
||||
- `Error` - Error messages
|
||||
- `Critical` - Critical failures
|
||||
|
||||
4. **Inject Specific Types**: Prefer injecting `ILoggerFactory` to create category-specific loggers rather than injecting a single shared logger.
|
||||
|
||||
5. **Use Categories**: Create loggers with meaningful category names:
|
||||
```csharp
|
||||
var logger = loggerFactory.CreateLogger("MyApp.Services.UserService");
|
||||
```
|
||||
|
||||
6. **Enable Correlation IDs**: For distributed tracing scenarios:
|
||||
```csharp
|
||||
builder.BoostWithCorrelationId()
|
||||
```
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
### Minimal Setup (Console Only)
|
||||
|
||||
```csharp
|
||||
services.AddEonaCatLogging(b => b.WriteToConsole());
|
||||
```
|
||||
|
||||
### Development Environment
|
||||
|
||||
```csharp
|
||||
services.AddEonaCatLogging(builder =>
|
||||
{
|
||||
builder
|
||||
.WithMinimumLevel(LogLevel.Debug)
|
||||
.WriteToConsole(useColors: true)
|
||||
.WriteToFile("./logs")
|
||||
.BoostWithMachineName()
|
||||
.BoostWithThreadId();
|
||||
});
|
||||
```
|
||||
|
||||
### Production Environment
|
||||
|
||||
```csharp
|
||||
services.AddEonaCatLogging("ProductionApp", builder =>
|
||||
{
|
||||
builder
|
||||
.WithMinimumLevel(LogLevel.Information)
|
||||
.WriteToFile("./logs", minimumLevel: LogLevel.Information)
|
||||
.WriteToElasticSearch("https://elastic.company.com")
|
||||
.WriteToSlack("https://hooks.slack.com/...")
|
||||
.BoostWithCorrelationId()
|
||||
.BoostWithMachineName()
|
||||
.BoostWithUser();
|
||||
});
|
||||
```
|
||||
## Diagnostics
|
||||
|
||||
```csharp
|
||||
public class DiagnosticsService
|
||||
{
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
|
||||
public DiagnosticsService(ILoggerFactory loggerFactory)
|
||||
{
|
||||
_loggerFactory = loggerFactory;
|
||||
}
|
||||
|
||||
public void PrintDiagnostics()
|
||||
{
|
||||
var diagnostics = _loggerFactory.GetDiagnostics();
|
||||
Console.WriteLine($"Total Logged: {diagnostics.TotalLoggedCount}");
|
||||
Console.WriteLine($"Total Dropped: {diagnostics.TotalDroppedCount}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Disposing of the Logger
|
||||
|
||||
The logger is registered as a Singleton in the DI container, so it will be automatically disposed when the application shuts down. You can also manually access and dispose it:
|
||||
|
||||
```csharp
|
||||
var loggerFactory = app.Services.GetRequiredService<ILoggerFactory>();
|
||||
await loggerFactory.DisposeAsync();
|
||||
```
|
||||
@@ -130,6 +130,8 @@ namespace EonaCat.LogStack.Test.Web
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
int onLogCounter = 0;
|
||||
var defaultColor = Console.ForegroundColor;
|
||||
builder.Services.AddEonaCatLogging();
|
||||
|
||||
|
||||
//_ = Task.Run(() =>
|
||||
//{
|
||||
|
||||
Reference in New Issue
Block a user