2

我的项目中使用任何外部资源(数据库、Web 服务调用等)的每个方法都必须进行日志记录。问题是我最终得到了很多以某种方式重复的代码。句子看起来一样,但它们的参数发生了变化(即方法名称)。

这似乎是重构我的代码的一个很好的点。除了 AOP 库之外,我想知道如何至少避免使用魔术字符串并编写一次性对象并将方法主体包装在 using 语句中,如下所示:

public void LoggedMethod(int param)
{
    using (new AutoLog())
    {
        // do whatever needs to be done
    }
}

我的AutoLog课程将是一次性的,它将在实例化和处置时编写一个日志调用。我知道我可能会(ab)StackTrace为此使用类,但是AFAIK这会大大减慢我的方法,因为这个特定的类非常缓慢。

我的日志条目应该是(当然所有这些都包含方法名称):

  • 方法调用的开始
  • 方法调用结束
  • (可选)方法参数 - 当然是序列化的(JSON?)
  • (可选)执行时间

问题

我应该如何实现我的AutoLog课程以尽可能快地工作?如果我还可以读取方法参数,那就更好了,这样我就可以序列化它们并记录它们。

4

1 回答 1

1

我的项目中使用任何外部资源(数据库、Web 服务调用等)的每个方法都必须进行日志记录

我简直不敢相信。您可能记录了太多

句子看起来一样,但它们的参数发生了变化(即方法名称)。

在这种情况下,您可能在代码中遗漏了一些通用抽象。根据我的经验,您应该:

对架构上高度相关的代码有一个通用的抽象。

例如,看看这篇关于命令处理程序的文章和这篇关于存储库和自定义查询的文章。两篇文章都围绕架构概念定义了一个通用接口。(您可能应该至少阅读第一篇文章,以了解我的其余答案)。

使用通用接口,可以很容易地用装饰器包装一整套相关操作。以这个装饰器为例,它可以包裹所有执行用例的操作:

public class LoggingCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand>
{
    private readonly ICommandHandler<TCommand> decoratee;
    private readonly ILogger logger;

    public LoggingCommandHandlerDecorator(
        ICommandHandler<TCommand> decoratee,
        ILogger logger)
    {
        this.decoratee = decoratee;
        this.logger = logger;
    }

    void ICommandHandler<TCommand>.Handle(TCommand command)
    {
        using (new AutoLog())
        {
            this.decoratee.Handle(command);
        }
    }
}

我应该如何实现我的 AutoLog 类以尽可能快地工作?

你知道吗,装饰器只是一个薄薄的包装器,几乎没有开销。使用这种方法,您通常拥有的所有方法参数现在都打包在一个对象中。这对于日志记录非常方便,因为您可以简单地将整个序列化为TCommandXML、JSon 或更易读的格式,并且您只需编写此代码一次。

并且由于您担心性能,因为这LoggingCommandHandlerDecorator<TCommand>是一个泛型类型,您可以做的是向该类型添加一个静态构造函数(静态构造函数在该接口的每个封闭泛型版本运行一次)并预编译一些代码,以便您有效地序列化该命令消息。绝对没有什么可以与之竞争的。甚至 AOP 框架在访问方法的参数时也使用反射并使用装箱构建动态数组。

请注意,像这样应用装饰器并没有什么“穷人的”。但是,当您的架构不遵循SOLID原则时,当您尝试这样做时,您很快就会遇到麻烦。这些原则是获得灵活且可维护的软件的关键。

于 2013-04-03T14:28:06.880 回答