如何使用现代 logback 取回 MDC "inheritance"?
How to get back MDC "inheritance" with modern logback?
在回到一个较旧的项目并四处更新其依赖项之后,我不得不意识到自版本 1.1.5
以来,logback 不再将 MDC 传播给子项目:https://github.com/qos-ch/logback/commit/aa7d584ecdb1638bfc4c7223f4a5ff92d5ee6273
此更改使大部分日志几乎无用。
虽然我可以理解链接问题中给出的论点,但我不明白为什么不能以更 向后兼容 的方式(通常是通常的方式)进行此更改在 java..).
Q:现在 正确 实现相同行为的方法是什么,除了必须对从 Runnable 到线程的所有内容进行子类化之外?
我看不出有什么直接的方法可以改回来。想到的两个选择是:
方式#1:包装所有Runnable
s
引入一个抽象class,它将MDC
从原来的Thread
复制到一个新的Thread
,并用它代替Runnable
public abstract class MdcAwareRunnable implements Runnable
{
private Map<String, String> originalMdc;
public MdcAwareRunnable()
{
this.originalMdc = MDC.getCopyOfContextMap();
}
@Override
public void run()
{
MDC.setContextMap(originalMdc);
runImpl();
}
protected abstract void runImpl();
/**
* In case some Runnable comes from external API and we can't change that code we can wrap it anyway.
*/
public static MdcAwareRunnable wrap(Runnable runnable)
{
return new MdcAwareRunnable()
{
@Override
protected void runImpl()
{
runnable.run();
}
};
}
}
如果某些 Runnable
来自您无法更改该代码的外部 API,请使用 wrap
辅助方法。
缺点:需要分析和更改整个代码。
方式 #2:乱用 slf4j 内部结构
恢复在该提交之前使用 InheritableThreadLocal
的原始 LogbackMDCAdapter
实现,并以其他名称将其放在代码中的某个位置。然后在启动前后的某个地方使用反射来覆盖 MDC.mdcAdapter 属性 和该自定义实现的实例。这显然是一个肮脏的 hack,但与 #1 相比它省去了很多麻烦。
注意:出于性能原因,它会从现有 LogbackMDCAdapter
继承您的复活版本,并仅覆盖所有具有旧实现的方法。有关详细信息,请参阅 LoggingEvent.java and LogbackMDCAdapter.getPropertyMap
内部方法。
方式#3:搞乱 logback jar(甚至更奇怪的选择)
这对我来说听起来是一个很糟糕的计划,但为了完整起见,它确实如此。
再次恢复原来的 LogbackMDCAdapter
但这次不重命名,编译它并覆盖 logback.jar 中的 .class 文件。
或通过重命名恢复原来的 LogbackMDCAdapter
,从 logback.jar 中删除 org.slf4j.impl.StaticMDCBinder
的 .class 文件并添加您自己的 class,这将 return LogbackMDCAdapter
的复活版本到 logback.jar 或你的代码。 MDC
似乎按名称绑定到 class 以创建要使用的 MDCAdapter
的实现。
或者您可以通过使用自定义 ClassLoader
实现类似的结果,它将 org.slf4j.impl.StaticMDCBinder
映射到您的 class 而不是 logback.jar 中的那个。注意:这可能无法在将添加自己的自定义类加载器的 Web 容器内实现。
方式四:误用 TurboFilter
ch.qos.logback.classic.Logger
在将记录事件传递给附加程序之前将其传递给过滤器。
方式五:修改日志编码器/提供者
虽然这意味着日志记录事件没有更新,但日志输出将是。
在回到一个较旧的项目并四处更新其依赖项之后,我不得不意识到自版本 1.1.5
以来,logback 不再将 MDC 传播给子项目:https://github.com/qos-ch/logback/commit/aa7d584ecdb1638bfc4c7223f4a5ff92d5ee6273
此更改使大部分日志几乎无用。
虽然我可以理解链接问题中给出的论点,但我不明白为什么不能以更 向后兼容 的方式(通常是通常的方式)进行此更改在 java..).
Q:现在 正确 实现相同行为的方法是什么,除了必须对从 Runnable 到线程的所有内容进行子类化之外?
我看不出有什么直接的方法可以改回来。想到的两个选择是:
方式#1:包装所有Runnable
s
引入一个抽象class,它将MDC
从原来的Thread
复制到一个新的Thread
,并用它代替Runnable
public abstract class MdcAwareRunnable implements Runnable
{
private Map<String, String> originalMdc;
public MdcAwareRunnable()
{
this.originalMdc = MDC.getCopyOfContextMap();
}
@Override
public void run()
{
MDC.setContextMap(originalMdc);
runImpl();
}
protected abstract void runImpl();
/**
* In case some Runnable comes from external API and we can't change that code we can wrap it anyway.
*/
public static MdcAwareRunnable wrap(Runnable runnable)
{
return new MdcAwareRunnable()
{
@Override
protected void runImpl()
{
runnable.run();
}
};
}
}
如果某些 Runnable
来自您无法更改该代码的外部 API,请使用 wrap
辅助方法。
缺点:需要分析和更改整个代码。
方式 #2:乱用 slf4j 内部结构
恢复在该提交之前使用 InheritableThreadLocal
的原始 LogbackMDCAdapter
实现,并以其他名称将其放在代码中的某个位置。然后在启动前后的某个地方使用反射来覆盖 MDC.mdcAdapter 属性 和该自定义实现的实例。这显然是一个肮脏的 hack,但与 #1 相比它省去了很多麻烦。
注意:出于性能原因,它会从现有 LogbackMDCAdapter
继承您的复活版本,并仅覆盖所有具有旧实现的方法。有关详细信息,请参阅 LoggingEvent.java and LogbackMDCAdapter.getPropertyMap
内部方法。
方式#3:搞乱 logback jar(甚至更奇怪的选择)
这对我来说听起来是一个很糟糕的计划,但为了完整起见,它确实如此。
再次恢复原来的 LogbackMDCAdapter
但这次不重命名,编译它并覆盖 logback.jar 中的 .class 文件。
或通过重命名恢复原来的 LogbackMDCAdapter
,从 logback.jar 中删除 org.slf4j.impl.StaticMDCBinder
的 .class 文件并添加您自己的 class,这将 return LogbackMDCAdapter
的复活版本到 logback.jar 或你的代码。 MDC
似乎按名称绑定到 class 以创建要使用的 MDCAdapter
的实现。
或者您可以通过使用自定义 ClassLoader
实现类似的结果,它将 org.slf4j.impl.StaticMDCBinder
映射到您的 class 而不是 logback.jar 中的那个。注意:这可能无法在将添加自己的自定义类加载器的 Web 容器内实现。
方式四:误用 TurboFilter
ch.qos.logback.classic.Logger
在将记录事件传递给附加程序之前将其传递给过滤器。
方式五:修改日志编码器/提供者 虽然这意味着日志记录事件没有更新,但日志输出将是。