mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
- 添加 JsonStringEnumConverter 支持枚举的驼峰命名转换 - 实现 ConfigurableLoggerFactory 的 IDisposable 接口确保资源正确释放 - 支持日志级别配置的前缀匹配功能(命名空间层级匹配) - 优化测试代码中的资源管理,使用 using 语句确保对象正确释放 - 修复 JsonLogFormatter 测试中的属性访问逻辑,使用 TryGetProperty 安全访问 - 将测试中的异常断言从 ArgumentNullException 更新为 ArgumentException
192 lines
6.6 KiB
C#
192 lines
6.6 KiB
C#
using System.IO;
|
||
using System.Text.Json;
|
||
using System.Text.Json.Serialization;
|
||
using GFramework.Core.Abstractions.logging;
|
||
using GFramework.Core.logging.appenders;
|
||
using GFramework.Core.logging.filters;
|
||
using GFramework.Core.logging.formatters;
|
||
|
||
namespace GFramework.Core.logging;
|
||
|
||
/// <summary>
|
||
/// 日志配置加载器
|
||
/// </summary>
|
||
public static class LoggingConfigurationLoader
|
||
{
|
||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||
{
|
||
PropertyNameCaseInsensitive = true,
|
||
ReadCommentHandling = JsonCommentHandling.Skip,
|
||
AllowTrailingCommas = true,
|
||
Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, allowIntegerValues: true) }
|
||
};
|
||
|
||
/// <summary>
|
||
/// 从 JSON 文件加载配置
|
||
/// </summary>
|
||
/// <param name="filePath">配置文件路径</param>
|
||
/// <returns>日志配置对象</returns>
|
||
public static LoggingConfiguration LoadFromJson(string filePath)
|
||
{
|
||
if (!File.Exists(filePath))
|
||
throw new FileNotFoundException($"Configuration file not found: {filePath}");
|
||
|
||
var json = File.ReadAllText(filePath);
|
||
var config = JsonSerializer.Deserialize<LoggingConfiguration>(json, JsonOptions);
|
||
|
||
return config ?? throw new InvalidOperationException("Failed to deserialize configuration.");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从 JSON 字符串加载配置
|
||
/// </summary>
|
||
/// <param name="json">JSON 字符串</param>
|
||
/// <returns>日志配置对象</returns>
|
||
public static LoggingConfiguration LoadFromJsonString(string json)
|
||
{
|
||
var config = JsonSerializer.Deserialize<LoggingConfiguration>(json, JsonOptions);
|
||
return config ?? throw new InvalidOperationException("Failed to deserialize configuration.");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据配置创建 Logger 工厂
|
||
/// </summary>
|
||
/// <param name="config">日志配置</param>
|
||
/// <returns>Logger 工厂</returns>
|
||
public static ILoggerFactory CreateFactory(LoggingConfiguration config)
|
||
{
|
||
return new ConfigurableLoggerFactory(config);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据配置创建 Appender
|
||
/// </summary>
|
||
internal static ILogAppender CreateAppender(AppenderConfiguration config)
|
||
{
|
||
var formatter = CreateFormatter(config.Formatter);
|
||
var filter = config.Filter != null ? CreateFilter(config.Filter) : null;
|
||
|
||
return config.Type.ToLowerInvariant() switch
|
||
{
|
||
"console" => new ConsoleAppender(formatter, useColors: config.UseColors, filter: filter),
|
||
|
||
"file" => new FileAppender(
|
||
config.FilePath ?? throw new InvalidOperationException("FilePath is required for File appender."),
|
||
formatter,
|
||
filter),
|
||
|
||
"rollingfile" => new RollingFileAppender(
|
||
config.FilePath ??
|
||
throw new InvalidOperationException("FilePath is required for RollingFile appender."),
|
||
config.MaxFileSize,
|
||
config.MaxFileCount,
|
||
formatter,
|
||
filter),
|
||
|
||
"async" => new AsyncLogAppender(
|
||
CreateAppender(config.InnerAppender ??
|
||
throw new InvalidOperationException("InnerAppender is required for Async appender.")),
|
||
config.BufferSize),
|
||
|
||
_ => throw new NotSupportedException($"Appender type '{config.Type}' is not supported.")
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据配置创建格式化器
|
||
/// </summary>
|
||
internal static ILogFormatter CreateFormatter(string formatterType)
|
||
{
|
||
return formatterType.ToLowerInvariant() switch
|
||
{
|
||
"default" => new DefaultLogFormatter(),
|
||
"json" => new JsonLogFormatter(),
|
||
_ => throw new NotSupportedException($"Formatter type '{formatterType}' is not supported.")
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据配置创建过滤器
|
||
/// </summary>
|
||
internal static ILogFilter CreateFilter(FilterConfiguration config)
|
||
{
|
||
return config.Type.ToLowerInvariant() switch
|
||
{
|
||
"loglevel" => new LogLevelFilter(
|
||
config.MinLevel ?? throw new InvalidOperationException("MinLevel is required for LogLevel filter.")),
|
||
|
||
"namespace" => new NamespaceFilter(
|
||
config.Namespaces?.ToArray() ??
|
||
throw new InvalidOperationException("Namespaces is required for Namespace filter.")),
|
||
|
||
"composite" => new CompositeFilter(
|
||
config.Filters?.Select(CreateFilter).ToArray() ??
|
||
throw new InvalidOperationException("Filters is required for Composite filter.")),
|
||
|
||
_ => throw new NotSupportedException($"Filter type '{config.Type}' is not supported.")
|
||
};
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 可配置的 Logger 工厂
|
||
/// </summary>
|
||
internal sealed class ConfigurableLoggerFactory : ILoggerFactory, IDisposable
|
||
{
|
||
private readonly ILogAppender[] _appenders;
|
||
private readonly LoggingConfiguration _config;
|
||
private bool _disposed;
|
||
|
||
public ConfigurableLoggerFactory(LoggingConfiguration config)
|
||
{
|
||
_config = config ?? throw new ArgumentNullException(nameof(config));
|
||
_appenders = config.Appenders.Select(LoggingConfigurationLoader.CreateAppender).ToArray();
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
if (_disposed)
|
||
return;
|
||
|
||
foreach (var appender in _appenders)
|
||
{
|
||
if (appender is IDisposable disposable)
|
||
{
|
||
disposable.Dispose();
|
||
}
|
||
}
|
||
|
||
_disposed = true;
|
||
}
|
||
|
||
public ILogger GetLogger(string name, LogLevel minLevel = LogLevel.Info)
|
||
{
|
||
// 检查是否有特定 Logger 的级别配置(支持前缀匹配)
|
||
var effectiveLevel = _config.MinLevel;
|
||
|
||
foreach (var kvp in _config.LoggerLevels)
|
||
{
|
||
// 精确匹配或前缀匹配(命名空间层级)
|
||
if (name == kvp.Key || name.StartsWith(kvp.Key + ".", StringComparison.Ordinal))
|
||
{
|
||
effectiveLevel = kvp.Value;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 如果没有 Appender,返回简单的 ConsoleLogger
|
||
if (_appenders.Length == 0)
|
||
{
|
||
return new ConsoleLogger(name, effectiveLevel);
|
||
}
|
||
|
||
// 如果只有一个 Appender 且是 ConsoleAppender,优化为 ConsoleLogger
|
||
if (_appenders.Length == 1 && _appenders[0] is ConsoleAppender)
|
||
{
|
||
return new ConsoleLogger(name, effectiveLevel);
|
||
}
|
||
|
||
// 返回 CompositeLogger
|
||
return new CompositeLogger(name, effectiveLevel, _appenders);
|
||
}
|
||
} |