// Copyright (c) 2026 GeWuYou // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. using GFramework.Core.Abstractions.Logging; using GFramework.Cqrs.Abstractions.Cqrs; namespace GFramework.Cqrs.Cqrs.Behaviors; /// /// 在 CQRS 请求管道中记录请求开始、完成、取消与失败日志。 /// /// 请求类型。 /// 响应类型。 /// /// 该行为保留在 GFramework.Core.Cqrs.Behaviors 命名空间以兼容现有调用点, /// 但实现已迁入 GFramework.Cqrs 程序集,避免继续由 GFramework.Core 承载 CQRS runtime 细节。 /// public sealed class LoggingBehavior : IPipelineBehavior where TRequest : IRequest { private readonly ILogger _logger = LoggerFactoryResolver.Provider.CreateLogger(nameof(LoggingBehavior)); /// /// 执行日志包装后的下一段请求处理逻辑。 /// /// 当前请求消息。 /// 后续处理委托。 /// 取消令牌。 /// 请求处理结果。 public async ValueTask Handle( TRequest message, MessageHandlerDelegate next, CancellationToken cancellationToken) { var requestName = typeof(TRequest).Name; var start = Stopwatch.GetTimestamp(); _logger.Debug($"Handling {requestName}"); try { var response = await next(message, cancellationToken); var elapsed = Stopwatch.GetElapsedTime(start); _logger.Debug($"Handled {requestName} successfully in {elapsed.TotalMilliseconds} ms"); return response; } catch (OperationCanceledException) { _logger.Warn($"Handling {requestName} was cancelled"); throw; } catch (Exception ex) { var elapsed = Stopwatch.GetElapsedTime(start); _logger.Error($"Error handling {requestName} after {elapsed.TotalMilliseconds} ms", ex); throw; } } }