Spring:不同ApplicationContext的不同日志记录行为

时间:2010-12-13 14:35:45

标签: spring logging javabeans

我正在开发一个使用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

感谢您的回复。

3 个答案:

答案 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>