【问题标题】:Logging: Associate logs with object being created日志记录:将日志与正在创建的对象关联
【发布时间】:2020-08-14 16:25:31
【问题描述】:

我有一个带有 MainService 的 Spring Boot 项目,它使用几个帮助方法和其他“微服务”来创建一个 FinalObject,最终使用 Hibernate/JPA 进行持久化。方法和服务可能会记录多条消息,我希望这些消息与记录事件发生时正在创建的对象相关联。

问题在于辅助方法和微服务无权访问finalObject 实例,因此即使记录了所有内容,也只会将捕获的异常保存为finalObject 属性——而不是警告消息或其他日志:

class FinalObject {
    private int value;
    private int price;
    private List<String> logs;
    ...
}
class MainService {
    @Autowired ValueService valueService;  // + other services

    void createFinalObject() {  // Main method
        FinalObject o1 = new FinalObject();
        try {
            o1.setValue(valueService.getValue("some argument"));
        }
        catch (Exception e) {
            log.error(e.toString());  // Logging using Log4j2
            o1.addLog(e.toString());  // If there's an exception, I can easily log it to a o1 attribute.
        }
        o1.setPrice(calculatePrice(o1.getValue()));
        ...
    }
    int calculatePrice(int value) {  // Helper method
        if (value > getMarketPrice())
            log.info("This is very valuable!");  // I need a way to associate this with o1!
        ...
        return price;
    }
}
// ValueService.java
int getValue(String arg) {
    if (arg.matches("\\d$"))
        log.warn("arg ends with a number");  // Must also be saved to o1!
    ...
    return value;
}

解决方案 1:到处传递o1

int calculatePrice(int value, FinalObject o1) {
    if (value > getMarketPrice()) {
        o1.addLog("This is very valuable!");  // Now I have access to o1 here
        log.info("This is very valuable!");
    }
    ...

解决方案 2:将 o1logs 属性作为可修改列表传递:

o1.setPrice(calculatePrice(o1.getValue(), o1.getLogs()));
...
int calculatePrice(int value, List<String> finalObjectLogs) {
    if (value > getMarketPrice()) {
        finalObjectLogs.add("This is very valuable!");  // Directly modify o1's logs attribute
        log.info("This is very valuable!");
    }
    ...

解决方案 3:添加 log4j2 数据库附加程序

一个更优雅的解决方案可能是向 log4j2 添加一个数据库附加程序。一个挑战是如何将这些日志与o1 关联起来。 FinalObject id 仅在createFinalObject() 的最后生成,当它保存到数据库时,所以在执行日志语句时我没有 id。

问题:

我怎样才能比我上面提到的方式更优雅地做到这一点?

或者:

如果解决方案 3 看起来不错,我该如何实施?

【问题讨论】:

    标签: java spring-boot hibernate jpa logging


    【解决方案1】:

    我很好奇。在您的示例中,方法计算价格似乎与任何对象都不相关,那么您为什么要在其中包含有关特定对象的信息?

    另一方面,如果您想将它与在单个请求中执行的其他操作相关联,我建议您查看 Log4j-Audit 描述的RequestContext。您不必使用 Log4j Audit 来实现这样的事情。它只是利用 Log4j ThreadContext 并定义使用的特定键。然后初始化 ThreadContext 中的值和请求的开头,并在请求结束时将其清除。您可以根据需要将项目添加到 ThreadContext。然后可以将 Log4j 配置为在每个日志事件中包含特定键。

    我应该注意到 Spring Cloud Sleuth 正在做一些我多年来一直在做的事情。为了将请求信息从一个服务传播到下一个服务,它们只需要在调用服务和服务启动时与 HTTP 标头进行转换。这就是 Log4j-Audit 示例 RequestContext 显示注释以将属性分类为 ClientServer、Local 或 Chained 的原因。然后它提供完成此操作所需的组件。

    【讨论】:

    • RequestContext 非常适合我的需求 - 谢谢!关于calculatePrice 方法的问题的答案是它确实与现实中的一个对象有关,但该示例没有正确显示这一点,因为我想让它尽可能短。很抱歉您的回答迟到了。
    【解决方案2】:

    您可以使用 spring sleuth 和日志过滤器来记录端点。 查看 Spring Sleuth 文档;

    https://docs.spring.io/spring-cloud-sleuth/docs/2.2.4.RELEASE/reference/html/

    您可以创建一个过滤器来记录每个端点,或记录其中一个。

    使用此记录器,您可以记录所需的类型。

    【讨论】:

    • 谢谢,这看起来很有希望。这对我来说是全新的,所以我需要更多时间来阅读它。同时:使用它来将日志与finalObject 关联的好方法是什么?现在我想我可以保存finalObjects 和带有traceId 属性的日志,我可以在检索finalObject 时使用它来获取正确的日志。这似乎是一个好方法,还是您有其他想法?
    • 在我看来,如果您使用数据库进行持久化,您可以使用对象的对象 ID,但是,如果您不使用,则不应仅为您的模型创建字段对于这种方法,但是如果这是一个可以维护的解决方案,看起来,好吧,如果你没有数据库,但是当你在可伸缩性方面变薄时,你创建的每个类并且你想要记录,你需要添加一个那时的traceId。所以,可能不是一个好的解决方案。但对于临时解决方案,是一个很好的临时解决方案。
    猜你喜欢
    • 2011-04-06
    • 2022-12-19
    • 2011-02-12
    • 1970-01-01
    • 2019-09-29
    • 1970-01-01
    • 2020-11-12
    • 2014-11-09
    • 2020-05-14
    相关资源
    最近更新 更多