diff --git a/GFramework.Core.Abstractions/Logging/LoggerFactoryResolver.cs b/GFramework.Core.Abstractions/Logging/LoggerFactoryResolver.cs new file mode 100644 index 00000000..a87a18da --- /dev/null +++ b/GFramework.Core.Abstractions/Logging/LoggerFactoryResolver.cs @@ -0,0 +1,250 @@ +namespace GFramework.Core.Abstractions.Logging; + +/// +/// 提供全局日志工厂访问入口。 +/// +/// +/// 该类型位于抽象层,是为了让上层模块可以在不依赖 GFramework.Core 实现程序集的前提下 +/// 获取日志记录器。默认 provider 会优先通过反射解析 GFramework.Core 中的控制台实现, +/// 若宿主未加载该程序集,则退回到静默 provider,避免抽象层形成实现层循环依赖。 +/// +public static class LoggerFactoryResolver +{ + private const string DefaultProviderTypeName = + "GFramework.Core.Logging.ConsoleLoggerFactoryProvider, GFramework.Core"; + + /// + /// 获取或设置当前日志工厂提供程序。 + /// + /// + /// 当赋值为 时抛出。 + /// + public static ILoggerFactoryProvider Provider + { + get => field ??= CreateDefaultProvider(); + set => field = value ?? throw new ArgumentNullException(nameof(value)); + } + + /// + /// 获取或设置新创建日志记录器的最小日志级别。 + /// + /// + /// 该属性直接代理到当前 ,确保调用方调整级别后立即影响后续创建的日志器。 + /// + public static LogLevel MinLevel + { + get => Provider.MinLevel; + set => Provider.MinLevel = value; + } + + private static ILoggerFactoryProvider CreateDefaultProvider() + { + if (Type.GetType(DefaultProviderTypeName, throwOnError: false) is { } providerType && + Activator.CreateInstance(providerType) is ILoggerFactoryProvider provider) + { + provider.MinLevel = LogLevel.Info; + return provider; + } + + return new SilentLoggerFactoryProvider(); + } + + /// + /// 当宿主未提供默认日志实现时使用的静默 provider。 + /// + private sealed class SilentLoggerFactoryProvider : ILoggerFactoryProvider + { + public LogLevel MinLevel { get; set; } = LogLevel.Info; + + public ILogger CreateLogger(string name) + { + return new SilentLogger(name); + } + } + + /// + /// 默认日志实现不可用时的 no-op 日志器。 + /// + private sealed class SilentLogger(string name) : ILogger + { + public string Name() + { + return name; + } + + public bool IsTraceEnabled() + { + return false; + } + + public bool IsDebugEnabled() + { + return false; + } + + public bool IsInfoEnabled() + { + return false; + } + + public bool IsWarnEnabled() + { + return false; + } + + public bool IsErrorEnabled() + { + return false; + } + + public bool IsFatalEnabled() + { + return false; + } + + public bool IsEnabledForLevel(LogLevel level) + { + return false; + } + + public void Trace(string msg) + { + } + + public void Trace(string format, object arg) + { + } + + public void Trace(string format, object arg1, object arg2) + { + } + + public void Trace(string format, params object[] arguments) + { + } + + public void Trace(string msg, Exception t) + { + } + + public void Debug(string msg) + { + } + + public void Debug(string format, object arg) + { + } + + public void Debug(string format, object arg1, object arg2) + { + } + + public void Debug(string format, params object[] arguments) + { + } + + public void Debug(string msg, Exception t) + { + } + + public void Info(string msg) + { + } + + public void Info(string format, object arg) + { + } + + public void Info(string format, object arg1, object arg2) + { + } + + public void Info(string format, params object[] arguments) + { + } + + public void Info(string msg, Exception t) + { + } + + public void Warn(string msg) + { + } + + public void Warn(string format, object arg) + { + } + + public void Warn(string format, object arg1, object arg2) + { + } + + public void Warn(string format, params object[] arguments) + { + } + + public void Warn(string msg, Exception t) + { + } + + public void Error(string msg) + { + } + + public void Error(string format, object arg) + { + } + + public void Error(string format, object arg1, object arg2) + { + } + + public void Error(string format, params object[] arguments) + { + } + + public void Error(string msg, Exception t) + { + } + + public void Fatal(string msg) + { + } + + public void Fatal(string format, object arg) + { + } + + public void Fatal(string format, object arg1, object arg2) + { + } + + public void Fatal(string format, params object[] arguments) + { + } + + public void Fatal(string msg, Exception t) + { + } + + public void Log(LogLevel level, string message) + { + } + + public void Log(LogLevel level, string format, object arg) + { + } + + public void Log(LogLevel level, string format, object arg1, object arg2) + { + } + + public void Log(LogLevel level, string format, params object[] arguments) + { + } + + public void Log(LogLevel level, string message, Exception exception) + { + } + } +} diff --git a/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs index 583c30c8..f6cbdafa 100644 --- a/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs +++ b/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs @@ -1,11 +1,11 @@ using GFramework.Core.Abstractions.Architectures; using GFramework.Core.Abstractions.Enums; +using GFramework.Core.Abstractions.Logging; using GFramework.Core.Abstractions.Model; using GFramework.Core.Abstractions.Systems; using GFramework.Core.Abstractions.Utility; using GFramework.Core.Architectures; using GFramework.Core.Logging; -using Microsoft.Extensions.DependencyInjection; namespace GFramework.Core.Tests.Architectures; @@ -714,4 +714,4 @@ public class ArchitectureComponentRegistryBehaviorTests return _context; } } -} \ No newline at end of file +} diff --git a/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs index 87978cd2..cdcde44d 100644 --- a/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs +++ b/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs @@ -5,6 +5,7 @@ using GFramework.Core.Abstractions.Cqrs; using GFramework.Core.Abstractions.Enums; using GFramework.Core.Abstractions.Environment; using GFramework.Core.Abstractions.Ioc; +using GFramework.Core.Abstractions.Logging; using GFramework.Core.Abstractions.Model; using GFramework.Core.Abstractions.Query; using GFramework.Core.Abstractions.Systems; diff --git a/GFramework.Core.Tests/Architectures/ArchitectureInitializationPipelineTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureInitializationPipelineTests.cs index 717e1b18..10945ad2 100644 --- a/GFramework.Core.Tests/Architectures/ArchitectureInitializationPipelineTests.cs +++ b/GFramework.Core.Tests/Architectures/ArchitectureInitializationPipelineTests.cs @@ -1,9 +1,9 @@ using GFramework.Core.Abstractions.Enums; using GFramework.Core.Abstractions.Events; +using GFramework.Core.Abstractions.Logging; using GFramework.Core.Architectures; using GFramework.Core.Environment; using GFramework.Core.Logging; -using Microsoft.Extensions.DependencyInjection; namespace GFramework.Core.Tests.Architectures; @@ -185,4 +185,4 @@ public class ArchitectureInitializationPipelineTests private sealed class BootstrapMarker { } -} \ No newline at end of file +} diff --git a/GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs index b0ef7262..943e2bfd 100644 --- a/GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs +++ b/GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs @@ -2,12 +2,12 @@ using System.Reflection; using GFramework.Core.Abstractions.Architectures; using GFramework.Core.Abstractions.Enums; using GFramework.Core.Abstractions.Lifecycle; +using GFramework.Core.Abstractions.Logging; using GFramework.Core.Abstractions.Model; using GFramework.Core.Abstractions.Systems; using GFramework.Core.Abstractions.Utility; using GFramework.Core.Architectures; using GFramework.Core.Logging; -using Microsoft.Extensions.DependencyInjection; namespace GFramework.Core.Tests.Architectures; @@ -460,4 +460,4 @@ public class ArchitectureLifecycleBehaviorTests return _context; } } -} \ No newline at end of file +} diff --git a/GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs index cfe0db79..493f3590 100644 --- a/GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs +++ b/GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs @@ -1,4 +1,5 @@ using GFramework.Core.Abstractions.Architectures; +using GFramework.Core.Abstractions.Logging; using GFramework.Core.Abstractions.Utility; using GFramework.Core.Architectures; using GFramework.Core.Logging; diff --git a/GFramework.Core.Tests/Architectures/PriorityServiceTests.cs b/GFramework.Core.Tests/Architectures/PriorityServiceTests.cs index 24fd2681..0499a4ab 100644 --- a/GFramework.Core.Tests/Architectures/PriorityServiceTests.cs +++ b/GFramework.Core.Tests/Architectures/PriorityServiceTests.cs @@ -1,5 +1,6 @@ using System.Reflection; using GFramework.Core.Abstractions.Bases; +using GFramework.Core.Abstractions.Logging; using GFramework.Core.Abstractions.Model; using GFramework.Core.Abstractions.Systems; using GFramework.Core.Abstractions.Utility; @@ -244,4 +245,4 @@ public class PriorityTestUtilityC : IPriorityTestUtility, IPrioritized public int Priority => 30; } -#endregion \ No newline at end of file +#endregion diff --git a/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs b/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs index 0e03059e..4104ccb1 100644 --- a/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs +++ b/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs @@ -1,6 +1,7 @@ using System.Reflection; using GFramework.Core.Abstractions.Bases; using GFramework.Core.Abstractions.Cqrs; +using GFramework.Core.Abstractions.Logging; using GFramework.Core.Ioc; using GFramework.Core.Logging; using GFramework.Core.Tests.Cqrs; diff --git a/GFramework.Core.Tests/State/StateMachineSystemTests.cs b/GFramework.Core.Tests/State/StateMachineSystemTests.cs index 0ea62de6..2c234238 100644 --- a/GFramework.Core.Tests/State/StateMachineSystemTests.cs +++ b/GFramework.Core.Tests/State/StateMachineSystemTests.cs @@ -1,5 +1,6 @@ using System.Reflection; using GFramework.Core.Abstractions.Enums; +using GFramework.Core.Abstractions.Logging; using GFramework.Core.Abstractions.State; using GFramework.Core.Abstractions.Systems; using GFramework.Core.Architectures; @@ -373,4 +374,4 @@ public class TestStateV5_2 : IState } } -#endregion \ No newline at end of file +#endregion diff --git a/GFramework.Core/Logging/LoggerFactoryResolver.cs b/GFramework.Core/Logging/LoggerFactoryResolver.cs deleted file mode 100644 index c15378d1..00000000 --- a/GFramework.Core/Logging/LoggerFactoryResolver.cs +++ /dev/null @@ -1,26 +0,0 @@ -using GFramework.Core.Abstractions.Logging; - -namespace GFramework.Core.Logging; - -/// -/// 日志工厂提供程序解析器,用于管理和提供日志工厂提供程序实例 -/// -public static class LoggerFactoryResolver -{ - /// - /// 获取或设置当前的日志工厂提供程序 - /// - /// - /// 日志工厂提供程序实例,默认为控制台日志工厂提供程序 - /// - public static ILoggerFactoryProvider Provider { get; set; } - = new ConsoleLoggerFactoryProvider(); - - /// - /// 获取或设置日志记录的最小级别 - /// - /// - /// 日志级别枚举值,默认为Info级别 - /// - public static LogLevel MinLevel { get; set; } = LogLevel.Info; -} \ No newline at end of file diff --git a/GFramework.Core/Properties/TypeForwarders.cs b/GFramework.Core/Properties/TypeForwarders.cs new file mode 100644 index 00000000..a71d9af2 --- /dev/null +++ b/GFramework.Core/Properties/TypeForwarders.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; +using GFramework.Core.Abstractions.Logging; + +[assembly: TypeForwardedTo(typeof(LoggerFactoryResolver))] diff --git a/GFramework.Core/Services/Modules/CqrsRuntimeModule.cs b/GFramework.Core/Services/Modules/CqrsRuntimeModule.cs index 9e7ad307..3fe37558 100644 --- a/GFramework.Core/Services/Modules/CqrsRuntimeModule.cs +++ b/GFramework.Core/Services/Modules/CqrsRuntimeModule.cs @@ -1,7 +1,7 @@ using GFramework.Core.Abstractions.Architectures; using GFramework.Core.Abstractions.Cqrs; using GFramework.Core.Abstractions.Ioc; -using GFramework.Core.Logging; +using GFramework.Core.Abstractions.Logging; using GFramework.Cqrs; using GFramework.Cqrs.Abstractions.Cqrs; diff --git a/GFramework.Cqrs.Tests/GlobalUsings.cs b/GFramework.Cqrs.Tests/GlobalUsings.cs index c47473b0..9f52a3dd 100644 --- a/GFramework.Cqrs.Tests/GlobalUsings.cs +++ b/GFramework.Cqrs.Tests/GlobalUsings.cs @@ -20,6 +20,7 @@ global using System.Reflection; global using System.Runtime.CompilerServices; global using System.Threading; global using System.Threading.Tasks; +global using System.Diagnostics; global using GFramework.Tests.Common; global using Microsoft.Extensions.DependencyInjection; global using Moq; diff --git a/GFramework.Cqrs.Tests/Mediator/MediatorAdvancedFeaturesTests.cs b/GFramework.Cqrs.Tests/Mediator/MediatorAdvancedFeaturesTests.cs index 257809f0..fe8f4413 100644 --- a/GFramework.Cqrs.Tests/Mediator/MediatorAdvancedFeaturesTests.cs +++ b/GFramework.Cqrs.Tests/Mediator/MediatorAdvancedFeaturesTests.cs @@ -1,3 +1,4 @@ +using GFramework.Core.Abstractions.Logging; using GFramework.Core.Architectures; using GFramework.Core.Ioc; using GFramework.Core.Logging; diff --git a/GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs b/GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs index e403735e..728f005a 100644 --- a/GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs +++ b/GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs @@ -1,4 +1,5 @@ using GFramework.Core.Abstractions.Architectures; +using GFramework.Core.Abstractions.Logging; using GFramework.Core.Architectures; using GFramework.Core.Command; using GFramework.Core.Ioc; diff --git a/GFramework.Cqrs.Tests/Mediator/MediatorComprehensiveTests.cs b/GFramework.Cqrs.Tests/Mediator/MediatorComprehensiveTests.cs index b0b510d6..423b1c9b 100644 --- a/GFramework.Cqrs.Tests/Mediator/MediatorComprehensiveTests.cs +++ b/GFramework.Cqrs.Tests/Mediator/MediatorComprehensiveTests.cs @@ -1,5 +1,6 @@ using GFramework.Core.Abstractions.Architectures; using GFramework.Core.Abstractions.Events; +using GFramework.Core.Abstractions.Logging; using GFramework.Core.Architectures; using GFramework.Core.Command; using GFramework.Core.Environment; diff --git a/GFramework.Core/Cqrs/Behaviors/LoggingBehavior.cs b/GFramework.Cqrs/Cqrs/Behaviors/LoggingBehavior.cs similarity index 71% rename from GFramework.Core/Cqrs/Behaviors/LoggingBehavior.cs rename to GFramework.Cqrs/Cqrs/Behaviors/LoggingBehavior.cs index 7230f53d..ccd9f0bf 100644 --- a/GFramework.Core/Cqrs/Behaviors/LoggingBehavior.cs +++ b/GFramework.Cqrs/Cqrs/Behaviors/LoggingBehavior.cs @@ -11,19 +11,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -using System.Diagnostics; using GFramework.Core.Abstractions.Logging; -using GFramework.Core.Logging; using GFramework.Cqrs.Abstractions.Cqrs; -namespace GFramework.Core.Cqrs.Behaviors; +namespace GFramework.Cqrs.Cqrs.Behaviors; /// -/// 日志记录行为类,用于在CQRS管道中记录请求处理的日志信息 -/// 实现IPipelineBehavior接口,为请求处理提供日志记录功能 +/// 在 CQRS 请求管道中记录请求开始、完成、取消与失败日志。 /// -/// 请求类型,必须实现IRequest接口 -/// 响应类型 +/// 请求类型。 +/// 响应类型。 +/// +/// 该行为保留在 GFramework.Core.Cqrs.Behaviors 命名空间以兼容现有调用点, +/// 但实现已迁入 GFramework.Cqrs 程序集,避免继续由 GFramework.Core 承载 CQRS runtime 细节。 +/// public sealed class LoggingBehavior : IPipelineBehavior where TRequest : IRequest { @@ -31,13 +32,12 @@ public sealed class LoggingBehavior : IPipelineBehavior)); /// - /// 处理请求并记录日志 - /// 在请求处理前后记录调试信息,处理异常时记录错误日志 + /// 执行日志包装后的下一段请求处理逻辑。 /// - /// 要处理的请求消息 - /// 下一个处理委托,用于继续管道执行 - /// 取消令牌,用于取消操作 - /// 处理结果的ValueTask + /// 当前请求消息。 + /// 后续处理委托。 + /// 取消令牌。 + /// 请求处理结果。 public async ValueTask Handle( TRequest message, MessageHandlerDelegate next, diff --git a/GFramework.Core/Cqrs/Behaviors/PerformanceBehavior.cs b/GFramework.Cqrs/Cqrs/Behaviors/PerformanceBehavior.cs similarity index 61% rename from GFramework.Core/Cqrs/Behaviors/PerformanceBehavior.cs rename to GFramework.Cqrs/Cqrs/Behaviors/PerformanceBehavior.cs index 35ab2978..1d13319d 100644 --- a/GFramework.Core/Cqrs/Behaviors/PerformanceBehavior.cs +++ b/GFramework.Cqrs/Cqrs/Behaviors/PerformanceBehavior.cs @@ -11,33 +11,34 @@ // See the License for the specific language governing permissions and // limitations under the License. -using System.Diagnostics; using GFramework.Core.Abstractions.Logging; -using GFramework.Core.Logging; using GFramework.Cqrs.Abstractions.Cqrs; -namespace GFramework.Core.Cqrs.Behaviors; +namespace GFramework.Cqrs.Cqrs.Behaviors; /// -/// 性能监控行为类,用于监控CQRS请求的执行时间 -/// 实现IPipelineBehavior接口,检测并记录执行时间过长的请求 +/// 在 CQRS 请求管道中监控处理耗时,并对长耗时请求发出告警。 /// -/// 请求类型,必须实现IRequest接口 -/// 响应类型 +/// 请求类型。 +/// 响应类型。 +/// +/// 该行为保留现有公开命名空间以维持消费端兼容性,但实现已迁入 GFramework.Cqrs 程序集。 +/// public sealed class PerformanceBehavior : IPipelineBehavior where TRequest : IRequest { + private const double SlowRequestThresholdMilliseconds = 500; + private readonly ILogger _logger = LoggerFactoryResolver.Provider.CreateLogger(nameof(PerformanceBehavior)); /// - /// 处理请求并监控执行时间 - /// 使用Stopwatch测量请求处理耗时,超过500ms时记录警告日志 + /// 统计当前请求处理耗时,并在超阈值时记录警告日志。 /// - /// 要处理的请求消息 - /// 下一个处理委托,用于继续管道执行 - /// 取消令牌,用于取消操作 - /// 处理结果的ValueTask + /// 当前请求消息。 + /// 后续处理委托。 + /// 取消令牌。 + /// 请求处理结果。 public async ValueTask Handle( TRequest message, MessageHandlerDelegate next, @@ -53,11 +54,10 @@ public sealed class PerformanceBehavior : IPipelineBehavior { var elapsed = Stopwatch.GetElapsedTime(start); - if (elapsed.TotalMilliseconds > 500) + if (elapsed.TotalMilliseconds > SlowRequestThresholdMilliseconds) { var requestName = typeof(TRequest).Name; - _logger.Warn( - $"Long Running Request: {requestName} ({elapsed.TotalMilliseconds:F2} ms)"); + _logger.Warn($"Long Running Request: {requestName} ({elapsed.TotalMilliseconds:F2} ms)"); } } } diff --git a/GFramework.Cqrs/GlobalUsings.cs b/GFramework.Cqrs/GlobalUsings.cs index 7f8c14d6..97f2d13a 100644 --- a/GFramework.Cqrs/GlobalUsings.cs +++ b/GFramework.Cqrs/GlobalUsings.cs @@ -4,3 +4,4 @@ global using System.Linq; global using System.Threading; global using System.Threading.Tasks; global using Microsoft.Extensions.DependencyInjection; +global using System.Diagnostics;