Spring: различное поведение при ведении журнала для разных ApplicationContext - PullRequest
2 голосов
/ 13 декабря 2010

Я работаю над приложением, которое использует Spring и slf4j. Это приложение использует больше ApplicationContext параллельно. Есть ли способ, чтобы эти разные ApplicationContexts использовали разные свойства ведения журнала? Таким образом, первый AC может войти в «x.txt», а второй в «y.txt».

Я не хотел бы использовать больше файлов свойств. Надлежащим способом было бы определить файл конфигурации Logger Bean в Spring XML, где я мог бы установить другую цель вывода для соответствующего свойства.

Например:

<bean id="LoggerBean" class="???">
     <property name="target" value="${target}" />
</bean>

Здесь я мог бы манипулировать целевой переменной из источника, что было бы очень удобно.

private static final Logger log = LoggerFactory.getLogger(MyClass.class);

Таким образом, LoggerFactory.getLogger будет использовать конфигурацию компонента LoggerBean для создания экземпляра класса Logger.

Мне нужен метод, в котором каждый ApplicationContext имеет собственный объект LoggerFactory с различными свойствами (например, разные выходные данные). Поэтому мне не пришлось бы переписывать текущий код.

Я использую ApplicationContexts, настроенный на тот же XML-файл конфигурации. Таким образом, эти ApplicationContexts используют те же классы. Из-за этого все Logger создаются из LoggerFactory с теми же именами классов, в которых они использовались. Все Logger создаются в форме LoggerFactory.getLogger(MyClass.class), так как эти классы одинаковы во всех ApplicationContext ("MyClass"), я не могу определить по-разному Loggers.

Спасибо за любой ответ.

Ответы [ 3 ]

4 голосов
/ 14 декабря 2010

Вы можете определить управляемый Spring компонент, который будет настраивать регистратор.Например, если вы используете logback для реализации API slf4j, этот класс загрузит указанный файл конфигурации ведения журнала в logback после того, как Spring установит его свойства:

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метод в вашем коде приложения.Чтобы гарантировать, что файлы конфигурации журналирования не наступают друг на друга, каждый из них должен определять регистраторы с разными именами.

2 голосов
/ 17 декабря 2010

Окончательное решение было следующим:

SLF4j и поддержка Logback MDC , которая содержит пары ключ / значение для каждого потока. Хотя основным преимуществом нашей проблемы является то, что дочерний поток автоматически наследует пары ключ / значение своего родителя, поэтому, если новый поток создается во время инициализации ApplicationContext, этот поток наследует эти пары из вызывающего потока. После этого вы можете включить эти сохраненные значения в шаблон сообщения журнала. Поэтому я поместил специальный идентификатор 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());
    }
}

Этот компонент является первым определенным Spring Bean в main.xml.

<bean class="com.myproblem.LoggerSeparator">
    <property name="contextID" value="${contextID}" />
</bean>
...

Этот класс устанавливает contextID в MD. 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();
...

Сообщения журнала регистрируются в одном файле, но теперь я могу разделить их по их уникальному идентификатору.

1 голос
/ 13 декабря 2010

Вы можете использовать пользовательские 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>
...