使用PostSharp使用通用guid记录嵌套的分布式函数调用

时间:2015-03-17 13:47:51

标签: c# postsharp

目前,多层系统的每个组件都使用PostSharp记录所有函数调用。

e.g。对WCF服务的应用程序调用可能会提示调用DataService(正在进行中)和AuditService(通过NServiceBus)

Application -> CaseService: getCaseWithAppointments() (via WCF)
  CaseService -> DataService: getCaseById()
  CaseService -> DataService: getCaseAppointments()
  CaseService -> AuditService: logCaseAccess() (via NServiceBus)

这将导致将四行写入日志表,但是稍后查看日志表时,无法识别logCaseAccess()调用是否作为封闭{{1}的一部分进行调用应用程序调用。

如果可以将这四行标识为一组,那将会更有用。

我可以看到应用程序如何生成可以作为参数传递给每个后续调用的Guid,但是这只能通过更改解决方案中每个函数的签名来实现吗?该解决方案已包含20多项服务,包括200多项OperationContracts,因此这将付出相当大的努力。

有没有办法修改每个服务,以便他们的每个函数都知道需要一个额外的参数(Guid)并将其传递给任何连续的调用(可能使用PostSharp方法边界)?

我已阅读此内容(Is there a way in Log4Net or NLog (or some other logger) to output logs in an execution-stack-nested XML or JSON format?),但不幸的是,它没有解决我的要求的分布式(和多线程)性质:

1 个答案:

答案 0 :(得分:1)

如果只有同步代码,那么通常通过在输出中包含当前线程ID来解决在日志中识别调用堆栈的问题。这通常可以在底层日志框架中配置。例如,使用log4net:

<layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="[%thread] %message%newline" />
</layout>

如果使用异步方法,则需要使用“逻辑调用上下文”。有一篇有用的文章讨论了解决异步方法的类似日志记录问题:http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html

您可以根据自己的需要调整文章中的示例。例如,您可以创建OnMethodBoundaryAspect和on方法条目,将GUID保存在调用上下文中(如果尚未存在)。由于标准的PostSharp日志记录不会在调用上下文中使用您的自定义GUID,因此您需要自己在方面中执行日志输出。

[Serializable]
public class TestAspect : OnMethodBoundaryAspect
{
    private const string Name = "LogId";

    public TestAspect()
    {
        this.ApplyToStateMachine = true;
    }

    public override void OnEntry(MethodExecutionArgs args)
    {
        object contextId = CallContext.LogicalGetData(Name);
        if (contextId == null)
        {
            contextId = Guid.NewGuid().ToString();
            CallContext.LogicalSetData(Name, contextId);
        }

        // Build the log message and send the output to the underlying framework
        Console.WriteLine("{0}: {1}", contextId, args.Method.Name);
    }
}
相关问题