目录
介绍
使用代码
介绍我们已经在前面的部分讨论了如何添加身份验证/授权。这部分解释了我如何将日志记录添加到WCF服务。
使用代码在上一部分中,我们在执行身份验证和授权时将面向方面的编程概念引入了我们的项目。现在我们将使用相同的概念来记录执行细节。基本上,我们将记录3点。错误(特别是异常)、执行时间和请求。
要记录数据,我们必须定义两个类来保存日志数据,分别位于WCFServices.Shared类库项目的Objects文件夹下的PerfLog和ErrorLog。
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将保存Service方法请求和响应之间的时间差(以毫秒为单位)。IsException属性将保存错误的类型。错误可能是由抛出的异常引起的,也可能是由任何其他因素(如验证)引起的。如果错误是由抛出的异常引起的,则此属性为true,如果错误不是由抛出的异常引起的,则是false。Messages属性保存有关错误的任何信息。这可以是Exception的Message属性,如果抛出了一个Exception,或者将验证返回的字符串数组中的字符串串联起来。ErrorContent属性是可选的,但通常如果抛出Exception,它可能包含Exception的StackTrace属性,该属性保存了修复错误的非常敏感和有用的信息。最后,ErrorDate属性保存错误的日期和时间信息。
现在是实现日志记录过程的时候了。首先测量执行时间,让我们把一个Stopwatch放到ServiceMethodAspect类:
private static readonly Stopwatch sw = new Stopwatch();
并在身份验证和授权成功后立即启动它,实例化并启动它。
if (sw.IsRunning)
sw.Stop();
sw.Start();
我们首先检查stopwatch是否已经在运行。这并不是真正需要的,但它是检查所有可能导致失败的事件的好方法。
我们的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 }
};
}
}
而且,作为最重要的部分,请注意我们在Service方法声明之前添加了ServiceMethodAspect属性。没有这个属性,我们所有的工作都将毫无意义,因为这个属性是将代码执行路由到我们的切面方法的关键。
https://www.codeproject.com/Articles/5326223/Improving-WCF-Service-Quality-Part-4-Logging