Spring正在重置我的日志记录配置-我该如何解决? [英] Spring is resetting my logging configuration - how do I work around this?

查看:92
本文介绍了Spring正在重置我的日志记录配置-我该如何解决?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Spring Batch作业,负责处理传入的客户文件.要求之一是完成日志记录,以按作业运行(由客户)分隔日志文件.

I have a Spring Batch job that is responsible for processing incoming customer files. One of the requirements is that logging is done to separate log files per job run (by customer).

在应用程序的主体中,我处理命令行参数,然后从那里动态创建FileAppender.

In my application's main, I process command line arguments and then dynamically create my FileAppender from there.

我的logback.xml:

My logback.xml:

<configuration>
    <appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="Console" />
    </root>
</configuration>

我添加附加程序的代码:

My code adding the appender:

    private static void setupFileAppender() {
        String logDir = fetchLogDir();
        LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
        String datePortion = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));

        FileAppender<ILoggingEvent> fileAppender = new FileAppender<>();
        fileAppender.setContext(loggerContext);
        fileAppender.setName("File");
        fileAppender.setFile(logDir + baseFileName + "-" + datePortion + ".log");
        fileAppender.setAppend(true);

        PatternLayoutEncoder encoder = new PatternLayoutEncoder();
        encoder.setContext(loggerContext);
        encoder.setPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg%n");
        encoder.start();

        fileAppender.setEncoder(encoder);
        fileAppender.start();

        Logger rootLogger = loggerContext.getLogger("root");
        rootLogger.addAppender(fileAppender);

        log.info("Logging configured.");
    }

从我的主目录(或从其调用)执行的任何日志语句均按预期记录到文件中.我可以在调试模式下进行深入研究,然后看到根记录器上有两个附加程序-这两个配置中的控制台"和文件"附加程序.但是,一旦运行SpringApplication.run命令,FileAppender就会消失.

Any log statements executed from my main (or calls from it) log to file as expected. I can drill down in debug mode and see that I have two appenders on the root logger - the "Console" and "File" appenders from the two configurations. However, once I run the SpringApplication.run command, the FileAppender disappears.

我逐步完成了SpringApplicaton.run(...)方法,发现Spring正在重置我的日志记录配置,并从logback.xml中重新加载它.

I stepped through the SpringApplicaton.run(...) method and what I've found is that Spring is resetting my logging configuration and reloading it from logback.xml.

从SpringApplication:

From SpringApplication:

try {
    // Create and configure the environment
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    configureEnvironment(environment, args);
    for (SpringApplicationRunListener runListener : runListeners) {
        runListener.environmentPrepared(environment);
    }

    ...

来自EventPublishingRunListener:

@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
    publishEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args,
        environment));
}

private void publishEvent(SpringApplicationEvent event) {
    this.multicaster.multicastEvent(event);
}

稍后再打几个电话,然后再打LoggingApplicationListener:

A couple of calls later, then LoggingApplicationListener:

@Override
public void onApplicationEvent(ApplicationEvent event) {
    if (event instanceof ApplicationStartedEvent) {
        onApplicationStartedEvent((ApplicationStartedEvent) event);
    }
    else if (event instanceof ApplicationEnvironmentPreparedEvent) {
        onApplicationPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
    }
}

private void onApplicationPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
    if (this.loggingSystem == null) {
        this.loggingSystem = LoggingSystem.get(event.getSpringApplication()
            .getClassLoader());
    }
    initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
}

protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
    if (System.getProperty(PID_KEY) == null) {
        System.setProperty(PID_KEY, new ApplicationPid().toString());
    }
    initializeEarlyLoggingLevel(environment);
    initializeSystem(environment, this.loggingSystem);
    initializeFinalLoggingLevels(environment, this.loggingSystem);
}

private void initializeSystem(ConfigurableEnvironment environment,
        LoggingSystem system) {
    LogFile logFile = LogFile.get(environment);
    String logConfig = environment.getProperty(CONFIG_PROPERTY);
    if (StringUtils.hasLength(logConfig)) {
        try {
            ResourceUtils.getURL(logConfig).openStream().close();
            system.initialize(logConfig, logFile);
        }
        catch (Exception ex) {
            this.logger.warn("Logging environment value '" + logConfig
                + "' cannot be opened and will be ignored "
                + "(using default location instead)");
            system.initialize(null, logFile);
        }
    }
    else {
        system.initialize(null, logFile);
    }
}

LogbackLoggingSystem(和AbstractLoggingSystem)中:

@Override
public void initialize(String configLocation, LogFile logFile) {
    getLogger(null).getLoggerContext().getTurboFilterList().remove(FILTER);
    super.initialize(configLocation, logFile);
}

@Override
public void initialize(String configLocation, LogFile logFile) {
    if (StringUtils.hasLength(configLocation)) {
        // Load a specific configuration
        configLocation = SystemPropertyUtils.resolvePlaceholders(configLocation);
        loadConfiguration(configLocation, logFile);
    }
    else {
        String selfInitializationConfig = getSelfInitializationConfig();
        if (selfInitializationConfig == null) {
            // No self initialization has occurred, use defaults
            loadDefaults(logFile);
        }
        else if (logFile != null) {
            // Self initialization has occurred but the file has changed, reload
            loadConfiguration(selfInitializationConfig, logFile);
        }
        else {
            reinitialize();
        }
    }
}

最后一个击中上方,而reinitialize()称为:

The last else is hit above, and reinitialize() is called:

@Override
protected void reinitialize() {
    getLoggerContext().reset();
    loadConfiguration(getSelfInitializationConfig(), null);
}

调用上下文重置将重置所有内容.事实是,挖掘loadConfiguration方法也将reset方法称为日志记录上下文.

The call to reset on the context is what resets everything. The thing is, that digging into the loadConfiguration method also calls the reset method the logging context.

关于如何解决Spring重新设置日志记录配置的任何想法?

Any ideas on how to get around Spring resetting my logging configuration?

仅供参考,我使用的是4.1.4.Spring的发行版.

FYI, I'm using 4.1.4.RELEASE version of Spring.

推荐答案

这听起来像将您对日志记录配置的自定义推迟到LoggingApplicationListener运行之后才能进行.

It sounds like deferring your customisation of the logging configuration until after LoggingApplicationListener has run should work.

LoggingApplicationListener响应ApplicationEnvironmentPreparedEvent执行其初始化,并且顺序为Ordered.HIGHEST_PRECEDENCE + 11.为了防止您的自定义配置被覆盖,您可以将您的自定义逻辑封装在SmartApplicationListener中,该SmartApplicationListener响应同一事件,但顺序较低,因此可以在LoggingApplicationListener之后运行:

LoggingApplicationListener performs its initialisation in response to an ApplicationEnvironmentPreparedEvent and has an order of Ordered.HIGHEST_PRECEDENCE + 11. To prevent your custom configuration from being overridden, you could encapsulate your customisation logic in a SmartApplicationListener that responds to the same event but with a lower order so it runs after LoggingApplicationListener:

public class CustomLoggingConfigurationApplicationListener implements
    SmartApplicationListener {

    @Override
    public void onApplicationEvent(ApplicationEvent event) {    
        // Customise the logging configuration
    }   

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 12;
    }

    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType);
    }

    @Override
    public boolean supportsSourceType(Class<?> sourceType) {
        return true;
    }

}

您可以创建侦听器并将其注册到应用程序的主要方法中:

You can create the listener and register it in your application's main method:

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        new SpringApplicationBuilder(Application.class)
                .listeners(new CustomLoggingConfigurationApplicationListener())
                .run(args);
    }
}

这篇关于Spring正在重置我的日志记录配置-我该如何解决?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆