Added dependency injection
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user