logging - 非静态 Slf4j MDC 模式

标签 logging slf4j

在我的系统中,我有一系列复杂的参与者,它们能够管理自己的状态。为了便于说明,我们假设它是一辆汽车:

class Car {
    private String registrationPlate;
    private int gear;

    public void gearUp() {
       int oldGear = gear++;
       log.info("Changed gear from {} to {} for vehicle {}", oldGear, gear, registrationPlate);
    }
}

每当我改变装备(或任何其他状态)时,我想在日志文件中添加一些注释。鉴于我的系统中有许多汽车,因此使用任何日志条目写出受影响汽车的车牌至关重要。

上面的代码假设开发人员始终记得在日志条目中包含车牌。事实上,开发人员经常忘记这一点,我们最终会得到省略了此详细信息的日志条目,这使得在日志中跟踪事件变得不可能——因为您不知道哪辆“汽车”执行了记录的操作。

<小时/>

映射诊断上下文的概念似乎完全符合我的简介;我想将一些额外的元数据附加到写入日志的每一行,太棒了!

然而,实际上这个实现对我来说并不是很有用。 MDC 被假定为某种静态线程特定上下文(由 ThreadLocal 在内部促进)。

鉴于我的系统是多线程的,并且每辆车最终可能会对池中的任意数量的线程执行操作。这样一来,开发者的负担就增加了:

    public void gearUp() {
       int oldGear = gear++;
       MDC.put("registrationPlate", registrationPlate);
       log.info("Changed gear from {} to {}", oldGear, gear);
       MDC.clear();
    }

但愿他们忘记调用MDC.clear(),否则该线程的下一次调用记录器可能会附加到错误的汽车(如果下一个日志记录语句也忘记了MDC.put("registrationPlate", ...))

<小时/>

我真正需要的是一个特定于 MDC 的记录器。这是我对 API 的奇特想法,它可以促进这一点:

private final Logger logger;

public Car(String registrationPlate) {
    this.registrationPlate = registrationPlate;

    this.logger = LoggerFactory.buildLogger(this)
        .withContext("registrationPlate", registrationPlate)
        .build();
}

真的宁愿不自己构建这个,这似乎是我应该已经可以使用的功能。我忽略了哪些替代方案?

最佳答案

MDC 实际上是为提供基于线程的内容(例如网站正在服务的用户请求)而设计的,因此虽然您可能能够让它执行您想要的操作(我有时会扭曲它来处理它并非真正设计的目的),我认为这并不是您真正想要的。

看来您实际上正在寻找让该类的每个实例都有自己的记录器。好吧,一种方法就是实际上这样做,将实例名称实际用作记录器名称的一部分:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Car {
    private final Logger logger;
    private final String registrationNumber;

    public Car(final String registrationNumber) {
        this.registrationNumber = registrationNumber;
        logger = LoggerFactory.getLogger("example.car." + registrationNumber);
    }

    public void logSomething() {
        logger.info("Logging something here");
    }
}

这有点奇怪,因为我们习惯于记录名称总是与正在记录的类相同,虽然这是一个有用的约定,但我认为如果您正在寻找的特定内容,可以调整它记录的方式不太一致。如果您使用缩写的记录器名称进行日志记录,这可能无法正常工作(例如,如果您为 %c 提供长度参数,则 Logback 使用,其中除最后一个之外的段可以缩写为单个字符) ,但是如果您的日志记录配置设置为使用完整的记录器名称,这应该会给您我认为您正在寻找的内容。

我认为唯一可行的其他方法是使用标记做一些事情,为每个类实例创建一个新标记。这可能也同样有效,并且 Logback 至少允许您将标记作为模式的一部分包含在内,尽管这需要开发人员记住在每个日志条目上使用标记。作为另一种选择,它可能值得探索。

(我在这个答案中的代码,例如,我特此将其奉献给公共(public)领域。)

关于logging - 非静态 Slf4j MDC 模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35428649/

相关文章:

azure - 在 azure 中,如何在汇总语句中连接字符串?

java - Storm : set log level for topology

unit-testing - 使 Logback 在 ERROR 级别的日志事件上抛出异常

java - 堆栈跟踪在日志文件中的一行上打印所有内容 (Java)

java - 这个日志语句有什么问题?

java - 无法显示 Level.FINE 日志

linux - 在 unix 中打印日志起始文本

C++ 线程安全、基于上下文流的日志记录

java - 我可以让 Log4J 暂时禁止记录特定消息吗?

java - Gradle intelliJ 插件 SLF4J : Class path contains multiple SLF4J bindings