11

我一直在与一家公司的代码库合作,该公司的政策是编写大量跟踪日志。所以几乎每个方法都有一段这样开头的代码:

String LOG_METHOD = "nameOfMethod(String,List<Long>):void";
if(logger.isTraceEnabled()) { 
    Object[] params = new Object[] { string, list };
    logger.trace(CompanyMessages.newMethodInstanceMessage(this, LOG_METHOD, params)); 
}

并像这样结束(在 -finally子句中或仅在方法的末尾:

if(logger.isTraceEnabled()) { 
    logger.trace(CompanyMessages.leaveMethodInstanceMessage(this, LOG_METHOD)); 
}

实际上还有更多代码,但这是基本思想。这使代码变得混乱,其他编码人员不断地用他们自己的解释来搞乱它,这些解释不使用特定的CompanyMessages类来格式化要由监控工具读取的消息。所以我正在寻找一种方法来摆脱上面的所有代码,只提供所有需要跟踪记录的方法,并带有如下注释:@LogBefore('logLevel')& @LogAfter('logLevel')

我选择这个解决方案的原因是为了让其他开发人员不必学习任何新东西,只需使用注释而不是代码。我在一个服务器环境中工作,我们在其中部署了数百个 Web 应用程序和数十个开发人员。所以我一直在寻找一种方法来在 Web 应用程序中实现这一点,而无需大量额外的编码或额外的大型库。这意味着我正在寻找一个小型、稳定的 AOP 实现,使用类似于我建议的注释,易于在每个 Web 应用程序中配置。性能也很重要。用 AOP 实现这个的最简单的例子是什么?

编辑:我确实找到了与我正在寻找的东西非常相似的东西,但这有几个问题。必须配置所有需要记录的类,这将比仅使用注释更占用资源。弹簧配置会<aop:aspectj-autoproxy/>解决这个问题吗?

4

3 回答 3

6

看起来面向方面的编程(AOP)真的可以帮助你。它非常擅长解决这些跨领域问题,例如日志记录和跟踪,并且它支持您需要的注释。

看看AspectJ或 Spring AOP。

它将涉及对 AOP 原则和您选择的 API 的一些学习,但绝对值得付出努力。尤其是日志记录和跟踪是您将遇到的第一批 AOP 教程之一,而且无需深入研究即可轻松完成。

于 2011-04-09T13:49:05.173 回答
5

注释和 AOP 点都是有效的。使用注释来提醒 AOP 框架有关日志记录。

我要做的另一件事是修复您的记录器。

你有:

String LOG_METHOD = "nameOfMethod(String,List<Long>):void"
if(logger.isTraceEnabled()) { 
    Object[] params = new Object[] { string, list };
    logger.trace(CompanyMessages.newMethodInstanceMessage(this, LOG_METHOD, params) ); 
}

相反,考虑这样的事情:

logger.trace(this, LOG_METHOD, string, list);

你可以像这样实现它:

public void trace(Object obj, Object args...) {
    if (parentLogger.isTraceEnabled()) {
        logger.trace(CompanyMessages.newMethodInstanceMessage(obj, LOG_METHOD, args);
    }
}

大多数日志记录实用程序都是在我们在 Java 中使用可变参数之前编写的,所以我们仍然可以看到您所编写的内容。

我们仍然希望保护在未启用时防止调用日志,但这样做的主要动机是因为过去大多数人都会做你所做的事情,或者更糟:

logger.trace("My message: " + string + " with list " + list);

无论是否启用跟踪,它都有一个昂贵的表达式。

但是通过利用可变参数,您可以获得两者。只需使用 MessageFormat 之类的东西(您可能已经在这样做了),您就可以轻松获得:

logger.trace("My message: {0} with list {1}", string, list);

在禁用跟踪的情况下,这是一个传递 3 个指针的廉价方法调用。因此,保护​​它并使代码混乱的动机要少得多。

大多数现代记录器都不能很好地覆盖,因此您通常必须封装它而不是简单地扩展它。

它不能直接解决您的问题,而是动态生成跟踪信息。但这是一个简单的中间立场,可以轻松地逐步清理您现有的代码库。

此外,还有 2 个其他选项。

一种是使用后处理器运行您的代码并将登录添加到它不存在的地方。这为您节省了手动输入的负担,但它确实使代码混乱(因为它仍然存在于任何地方)。

二是在编译时使用注解处理器。这是更复杂的。但它所做的是在编译期间,它会在编译时使用信息贯穿并扩充您的类。好消息是你的代码是干净的(也许是为了注释),而且所有的工作都是在编译时完成的。没有运行时影响,也没有对象工厂的花哨类加载器。构建完成后,您可以将处理器扔掉,在运行时根本不需要它。

有一个项目,我不知道他的名字,它利用了这一点。它会在编译时自动将 setter/getter 添加到您的代码中。我听说过它的好消息。

AOP 框架可能会在编译时为您执行此操作,我对它们还不够熟悉,但无论哪种方式,该技术都值得探索。

不过,至少要包装您的记录器。它是增量的、安全的,并且会逐渐清理您的代码并帮助您在注释通常不适合您的情况下进行日志记录。

于 2011-04-09T16:28:56.057 回答
3

我不认为注释是解决方案。注释一个类或实例意味着在运行时给类额外的信息,它本身并没有做任何事情。您将需要一些代码来处理带有注释的类,并在运行时根据这些注释在每个方法之前和之后添加代码。

所以没有办法添加注释并准备好了,你的类开始记录他们的方法。

解决方案应该是 AOP——这正是 AOP 最初发明的问题。在每个方法上定义类/方法/动作,你就解决了问题。

好吧,也许你可以让它在运行时使用注释和修改类,但你最终会得到自制的 AOP :-)

于 2011-04-09T12:33:48.683 回答