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