如何在 OSGi 中实现一个具有 class 的包,它将值放入 ThreadContext 以使这些值对其他包可见
How to implement in OSGi a bundle that has a class which puts values into ThreadContext to have these values be visible to other bundles
总结一下这个问题,我在整个项目中使用的捆绑包中有一个通用的 class。
此 class 是一个方面 class,它将值放入 ThreadContext(log4j2) 以便我们的日志记录将相应的会话详细信息放入日志中。
我发现 ThreadContext 的工作方式类似于 ThreadLocal 变量。由于 OSGi 对每个包都有不同的 class 加载器,callers/users 的 ThreadContext 值根本无法看到这些值。
参考资料:
Effect of ThreadLocals and side-by-side classloading
我期望的是仍然有一个包含公共代码的包,但其他包仍然能够看到放入 ThreadContext 的值。我不确定这是否可能。
编辑: 添加示例代码
Bundle1 (Common Code)
@Configurable
@Aspect
public class AspectLogger {
@Before("within(@org.springframework.stereotype.Controller *)")
public void beforeControllerAdvice(JoinPoint joinPoint) {
Object[] paramValues = null;
paramValues = joinPoint.getArgs();
Object request = null;
for (Object arg : paramValues) {
if (arg instanceof RenderRequest) {
request = arg;
} else if (arg instanceof ResourceRequest) {
request = arg;
} else if (arg instanceof ActionRequest) {
request = arg;
}
}
if (request != null) {
String transactionID = UUID.randomUUID().toString();
ThreadContext.put("transactionID", transactionID);
}
}
}
Bundle 2,3...n (Using this Aspect as a Bean)
<bean id="aspectLogger" class="shared.bundle.common.AspectLogger" />
Log4j2 configuration using this value for logger appenders (Problem values are empty in the logs)
<PatternLayout>
<Pattern>%-5p | %d{yyyy-MM-dd HH:mm:ss} | [%t] %c{2} (%F:%L) [transactionID: %X{transactionID}] - %m%n</Pattern>
</PatternLayout>
从调试器的角度来看,我可以看到控制器的线程和方面的线程基本相同。但我仍然没有得到这些值,我什至在 @Controller
级别尝试了 ThreadContext.get("transactionID")
但它是空的。
ThreadLocals 和 class 加载器是正交的。为每个包使用不同的 class 加载器并没有说明哪些线程可能正在调用从这些包加载的 classes。因此,只要您在调用另一个包中的代码之前设置 ThreadLocal,该代码就能够看到该线程的 ThreadLocal。
所以问题的原因是 log4j 嵌入到每个 war 文件中,然后部署到 OSGi。嵌入的 类 由相应的包加载。所以他们生活在不同的类加载器中。
在 OSGi 中嵌入日志不是一个好主意。原因之一是您无法拥有中央日志记录配置。另一个原因是上面的问题。
您可以查看 pax-logging 或 felix logback 支持作为适当的 OSGi 日志记录解决方案。两者都提供 log4j API。
在您的 war files 中,您需要定义日志记录 api 包的导入。然后所有 war 都应该使用来自同一个委托类加载器的 ThreadContext,问题应该得到解决。
总结一下这个问题,我在整个项目中使用的捆绑包中有一个通用的 class。 此 class 是一个方面 class,它将值放入 ThreadContext(log4j2) 以便我们的日志记录将相应的会话详细信息放入日志中。
我发现 ThreadContext 的工作方式类似于 ThreadLocal 变量。由于 OSGi 对每个包都有不同的 class 加载器,callers/users 的 ThreadContext 值根本无法看到这些值。
参考资料:
Effect of ThreadLocals and side-by-side classloading
我期望的是仍然有一个包含公共代码的包,但其他包仍然能够看到放入 ThreadContext 的值。我不确定这是否可能。
编辑: 添加示例代码
Bundle1 (Common Code)
@Configurable
@Aspect
public class AspectLogger {
@Before("within(@org.springframework.stereotype.Controller *)")
public void beforeControllerAdvice(JoinPoint joinPoint) {
Object[] paramValues = null;
paramValues = joinPoint.getArgs();
Object request = null;
for (Object arg : paramValues) {
if (arg instanceof RenderRequest) {
request = arg;
} else if (arg instanceof ResourceRequest) {
request = arg;
} else if (arg instanceof ActionRequest) {
request = arg;
}
}
if (request != null) {
String transactionID = UUID.randomUUID().toString();
ThreadContext.put("transactionID", transactionID);
}
}
}
Bundle 2,3...n (Using this Aspect as a Bean)
<bean id="aspectLogger" class="shared.bundle.common.AspectLogger" />
Log4j2 configuration using this value for logger appenders (Problem values are empty in the logs)
<PatternLayout>
<Pattern>%-5p | %d{yyyy-MM-dd HH:mm:ss} | [%t] %c{2} (%F:%L) [transactionID: %X{transactionID}] - %m%n</Pattern>
</PatternLayout>
从调试器的角度来看,我可以看到控制器的线程和方面的线程基本相同。但我仍然没有得到这些值,我什至在 @Controller
级别尝试了 ThreadContext.get("transactionID")
但它是空的。
ThreadLocals 和 class 加载器是正交的。为每个包使用不同的 class 加载器并没有说明哪些线程可能正在调用从这些包加载的 classes。因此,只要您在调用另一个包中的代码之前设置 ThreadLocal,该代码就能够看到该线程的 ThreadLocal。
所以问题的原因是 log4j 嵌入到每个 war 文件中,然后部署到 OSGi。嵌入的 类 由相应的包加载。所以他们生活在不同的类加载器中。
在 OSGi 中嵌入日志不是一个好主意。原因之一是您无法拥有中央日志记录配置。另一个原因是上面的问题。
您可以查看 pax-logging 或 felix logback 支持作为适当的 OSGi 日志记录解决方案。两者都提供 log4j API。
在您的 war files 中,您需要定义日志记录 api 包的导入。然后所有 war 都应该使用来自同一个委托类加载器的 ThreadContext,问题应该得到解决。