65.9K
CodeProject 正在变化。 阅读更多。
Home

提高 WCF 服务质量(第四部分 - 日志记录)

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2022 年 2 月 28 日

CPOL

3分钟阅读

viewsIcon

6079

如何向 WCF 服务添加日志记录

引言

在上一部分中,我们讨论了如何添加身份验证/授权,此处。 本部分解释了如何将日志记录添加到 WCF 服务。

Using the Code

在执行身份验证和授权时,我们在上一部分中引入了 面向方面的编程 概念到我们的项目中。 现在我们将使用相同的概念来记录执行细节。 基本上,我们将记录 3 个点。 错误(特别是异常)、执行时间和请求。

要记录数据,我们必须定义两个类来保存日志数据,分别为 PerfLogErrorLog,位于 WCFServices.Shared 类库项目的 Objects 文件夹下。

public class PerfLog
{
    public string Path { get; set; }
    public string InData { get; set; }
    public string OutData { get; set; }
    public long ExecutionTime { get; set; }
}
public class ErrorLog
{
    public string Path { get; set; }
    public bool IsException { get; set; }
    public string Messages { get; set; }
    public string ErrorContent { get; set; }
    public string InData { get; set; }
    public DateTime ErrorDate { get; set; }
}

让我们在 WCFService.Utils 项目的 Logging 文件夹下添加一个新类 LogManager,并按如下方式进行装饰

public async void CreateErrorLog(ErrorLog logItem)
{
    // Save data to database
}
public async void CreatePerfLog(PerfLog logItem)
{
    // Save data to database
}

这些方法被标记为 async,原因有两个。 首先,我们不想花时间在日志记录上以减少响应时间。 其次,即使日志记录失败,这也不是我们主要关注的问题之一。 未能保存日志将导致数据丢失,但不一定需要中断执行。 请记住,我们的首要目的是为客户端提供服务。 快速而准确。 其他一切都处于次要地位,以提高质量。

关于日志记录记录属性的简要说明。 Path 属性将保存调用的服务和服务方法的名称。 InData 将保存馈送到服务方法的输入参数,而 OutData 将保存服务方法的响应。 ExecutionTime 将以毫秒为单位保存服务方法请求和响应之间的时间差。 IsException 属性将保存错误的类型。 错误可能由抛出的异常引起,也可能由任何其他因素引起,例如验证。 如果错误是由抛出的异常引起的,则此属性将为 true,如果错误不是由抛出的异常引起的,则为 falseMessages 属性保存有关错误的任何信息。 这可以是 ExceptionMessage 属性(如果抛出 Exception)或从验证返回的字符串数组的串联字符串。 ErrorContent 属性是可选的,但通常它可以保存 ExceptionStackTrace 属性(如果抛出 Exception),该属性保存了相当敏感且有用的信息来修复错误。 最后,ErrorDate 属性保存错误的日期和时间信息。

现在是实现日志记录过程的时候了。 首先,要测量执行时间,让我们将 Stopwatch 放入 ServiceMethodAspect 类中

private static readonly Stopwatch sw = new Stopwatch();

并在身份验证和授权成功后立即启动它,实例化并启动它。

if (sw.IsRunning)
    sw.Stop();
sw.Start();

我们首先检查秒表是否已经在运行。 这实际上不是必需的,但这是一个很好的衡量标准,可以检查所有可能导致失败的事件。

我们的 OnExit 方法将如下所示

public override void OnExit(MethodExecutionArgs args)
{
    PerfLog item = new PerfLog
    {
        Path = $"{args.Method.DeclaringType.FullName}.{args.Method.Name}",
        InData = JsonConvert.SerializeObject(args.Arguments),
        OutData = JsonConvert.SerializeObject(args.ReturnValue),
        ExecutionTime = sw.ElapsedMilliseconds
    };
    sw.Stop();
    LogManager mgr = new LogManager();
    mgr.CreatePerfLog(item);
    base.OnExit(args);
}

OnException 方法将如下所示

public override void OnException(MethodExecutionArgs args)
{
    ErrorLog item = new ErrorLog
    {
        Path = $"{args.Method.DeclaringType.FullName}.{args.Method.Name}",
        InData = JsonConvert.SerializeObject(args.Arguments),
        IsException = true,
        Message = args.Exception.Message,
        ErrorContent = JsonConvert.SerializeObject(args.Exception),
        ErrorDate = DateTime.Now
    };
    sw.Stop();
    LogManager mgr = new LogManager();
    mgr.CreateErrorLog(item);
    args.FlowBehavior = FlowBehavior.Return;
    args.ReturnValue = new BaseResponse
    {
        IsException = true,
        IsSuccess = false,
        Messages = new string[] { args.Exception.Message }
    };
    base.OnException(args);
}

由于我们将复杂对象存储为 string,因此我们需要序列化数据。 我的选择是使用 JSON,但任何序列化过程都可以正常工作。 如果您也想使用 JSON 序列化,则应使用 NuGet 将 NewtonSoft.Json 添加到您的项目中。

如果我们还想记录验证错误,我们应该将相同的日志记录过程添加到我们的服务方法中。 例如,这仅在 CreatePlayer 服务方法中演示。

[ServiceMethodAspect]
public CreatePlayerResponse CreatePlayer(CreatePlayerRequest request)
{
    try
    {
        CreatePlayerValidator validator = new CreatePlayerValidator();
        ValidationResult valResult = validator.Validate(request);
        if (!valResult.IsValidated)
        {
            ErrorLog item = new ErrorLog
            {
                Path = $"{System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.
                          FullName}.{System.Reflection.MethodBase.GetCurrentMethod().Name}",
                InData = JsonConvert.SerializeObject(request),
                IsException = false,
                Message = "Validation failed",
                ErrorContent = JsonConvert.SerializeObject(valResult.Messages),
                ErrorDate = DateTime.Now
            };
            LogManager mgr = new LogManager();
            mgr.CreateErrorLog(item);
            return new CreatePlayerResponse
            {
                IsException = false,
                IsSuccess = false,
                Messages = valResult.Messages.ToArray()
            };
        }
        return new CreatePlayerResponse
        {
            PlayerId = PlayerRepository.CreateNewPlayer
            (request.Name, request.DateOfBirth, request.Height, request.Weight, request.Club),
            IsException = false,
            IsSuccess = true,
            Messages = new string[] { "Operation successful" }
        };
    }
    catch (Exception ex)
    {
        return new CreatePlayerResponse
        {
            IsException = true,
            IsSuccess = false,
            Messages = new string[] { ex.Message }
        };
    }
}

而且,作为最重要的部分,请注意我们在服务方法声明之前添加了 ServiceMethodAspect 属性。 没有此属性,我们所有的工作都将毫无意义,因为此属性是将代码执行路由到我们的方面方法的关键。

历史

  • 2022 年 2 月 28 日:初始版本
© . All rights reserved.