我正在开发一个使用Spring和slf4j的应用程序。该应用程序并行使用更多的ApplicationContext。 有没有办法让这些不同的ApplicationContexts使用不同的日志记录属性? 因此第一个AC可以登录到“x.txt”,而第二个AC可以登录到“y.txt”。
我不想使用更多属性文件。适当的方法是在Spring XML配置文件中定义Logger Bean,我可以为相应的属性设置不同的输出目标。
例如:
<bean id="LoggerBean" class="???">
<property name="target" value="${target}" />
</bean>
在这里,我可以从源操作目标变量,这将非常方便。
private static final Logger log = LoggerFactory.getLogger(MyClass.class);
因此LoggerFactory.getLogger
将使用LoggerBean
bean配置来实例化Logger类。
我需要一种方法,其中每个ApplicationContext都有一个具有不同属性的自己的LoggerFactory
对象(如不同的目标输出)。所以我不必重写当前的代码。
我使用由同一个xml配置文件配置的ApplicationContexts
。所以这些ApplicationContexts使用
同一个班级。因此,所有Logger
都是从LoggerFactory
实例化的,其中使用的是相同的类名。
所有Logger都由LoggerFactory.getLogger(MyClass.class)
表单实例化,因为这些类在所有ApplicationContext
(“MyClass”)中都是相同的,我无法定义不同名称Loggers
。
感谢您的回复。
答案 0 :(得分:4)
您可以定义一个Spring管理的bean来配置记录器。例如,假设您使用logback实现slf4j API,此类将在Spring设置其属性后将指定的日志记录配置文件加载到logback中:
public class LogBackConfigurer implements InitializingBean {
private Resource location;
public void setLocation(Resource location) {
this.location = location;
}
public void afterPropertiesSet() throws Exception {
JoranConfigurator configurator = new JoranConfigurator();
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
configurator.setContext(loggerContext);
configurator.doConfigure(location.getInputStream());
}
}
在每个Spring配置文件中,您希望拥有不同的日志记录配置,请使用不同的日志记录配置文件位置定义如下所示的bean。
<bean class="com.example.log.LogBackConfigurer">
<property name="location" value="classpath:a-logback.xml"/>
</bean>
该类修改单个应用程序范围的日志记录上下文,这是必需的,因为您要在应用程序代码中调用静态Logger工厂方法。为确保日志记录配置文件不会相互踩踏,它们每个都必须定义不同名称的记录器。
答案 1 :(得分:2)
最终解决方案如下:
SLF4j和Logback支持MDC,其中包含每个线程的键/值对。虽然我们的问题的主要优点是子线程自动继承其父级的键/值对,因此如果在ApplicationContext初始化期间创建新的Thread,该Thread将从调用线程继承这些对。之后,您可以在日志消息模式中包含这些存储的值。 所以我在加载ApplicationContext之前在MDC中放了一个特殊的ApplicationContext标识符。当使用Logger字段实例化类时,这些字段将获取其唯一标识符,该标识符包含在日志消息模式中。
<Pattern>[%X{contextID}] - [%thread] - %date{dd/MM/yyyy HH:mm:ss} %level %msg%n</Pattern>
LoggerSeparator.java
public class LoggerSeparator implements InitializingBean{
private Integer contextID;
public LoggerSeparator() {}
public void setContextID(int id) {
this.contextID = id;
}
@Override
public void afterPropertiesSet() throws Exception {
if ( contextID != null )
MDC.put("contextID", contextID.toString());
}
}
这个bean是main.xml中第一个定义的Spring Bean。
<bean class="com.myproblem.LoggerSeparator">
<property name="contextID" value="${contextID}" />
</bean>
...
该类在MD中设置contextID。 contextID来自源代码。
...
Properties props = new Properties();
props.put("contextID", contextID);
PropertyPlaceholderConfigurer conf = new PropertyPlaceholderConfigurer();
conf.setProperties(props);
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
context.addBeanFactoryPostProcessor(conf);
context.setConfigLocation("beans/main.xml");
context.refresh();
...
日志消息记录在一个文件中,但现在我可以用它们的唯一标识符将它们分开。
答案 2 :(得分:1)
您可以使用自定义FactoryBean将记录器添加到上下文中:
public class Slf4jLoggerFactoryBean implements FactoryBean<Logger> {
private String loggerName;
public Logger getObject() throws Exception {
return LoggerFactory.getLogger(loggerName);
}
public Class<?> getObjectType() {
return Logger.class;
}
public boolean isSingleton() {
return true;
}
public void setLoggerName(String loggerName) {
this.loggerName = loggerName;
}
}
然后XML将如下所示:
<bean id="LoggerBean" class="com.example.Slf4jLoggerFactoryBean">
<property name="target" value="${target}" />
</bean>