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





5.00/5 (1投票)
如何向 WCF 服务添加日志记录
引言
在上一部分中,我们讨论了如何添加身份验证/授权,此处。 本部分解释了如何将日志记录添加到 WCF 服务。
Using the Code
在执行身份验证和授权时,我们在上一部分中引入了 面向方面的编程 概念到我们的项目中。 现在我们将使用相同的概念来记录执行细节。 基本上,我们将记录 3 个点。 错误(特别是异常)、执行时间和请求。
要记录数据,我们必须定义两个类来保存日志数据,分别为 PerfLog
和 ErrorLog
,位于 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
,如果错误不是由抛出的异常引起的,则为 false
。 Messages
属性保存有关错误的任何信息。 这可以是 Exception
的 Message
属性(如果抛出 Exception
)或从验证返回的字符串数组的串联字符串。 ErrorContent
属性是可选的,但通常它可以保存 Exception
的 StackTrace
属性(如果抛出 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 日:初始版本