Disable Log4j2's shutdown hook by default

Previously, Log4j2's own shutdown hook was only disabled when Log4j2
detected javax.servlet.Servlet on the classpath and, therefore,
determined that it was running in a web application. In an application
without Servlet on the classpath, this could lead to both Log4j2's shut
down hook and and logging system's shutdown handler both stopping
Log4j2. This could result in a failure as the second attempt at stopping
would result in reinitialization which would fail as the JVM is already
shutting down.

This commit introduces a new Log4j2 PropertySource implementation,
registered via META-INF/services, that sets the
log4j.shutdownHookEnabled property to false. This will ensure that
Log4j2's own shutdown hook is disabled by default whenever Spring Boot
is on the classpath and not just in Servlet-based web applications.

Fixes gh-26953
pull/27157/head
Andy Wilkinson 3 years ago
parent 0a2fe3562d
commit cf940fd374

@ -30,13 +30,12 @@ import org.apache.logging.log4j.core.config.plugins.Plugin;
* *
* <ol> * <ol>
* <li>Prevent logger warnings from being printed when the application first starts. * <li>Prevent logger warnings from being printed when the application first starts.
* <li>Disable its shutdown hook
* </ol> * </ol>
* *
* This factory is ordered last and is triggered by a {@code log4j2.springboot} classpath * This factory is ordered last and is triggered by a {@code log4j2.springboot} classpath
* resource (which is bundled in this jar). If the {@link Log4J2LoggingSystem} is active, * resource (which is bundled in this jar). If the {@link Log4J2LoggingSystem} is active,
* a custom {@link DefaultConfiguration} is returned with the expectation that the system * a {@link DefaultConfiguration} is returned with the expectation that the system will
* will later re-initialize Log4J2 with the correct configuration file. * later re-initialize Log4J2 with the correct configuration file.
* *
* @author Phillip Webb * @author Phillip Webb
* @since 1.5.0 * @since 1.5.0
@ -57,15 +56,7 @@ public class SpringBootConfigurationFactory extends ConfigurationFactory {
if (source == null || source == ConfigurationSource.NULL_SOURCE) { if (source == null || source == ConfigurationSource.NULL_SOURCE) {
return null; return null;
} }
return new SpringBootConfiguration(); return new DefaultConfiguration();
}
private static final class SpringBootConfiguration extends DefaultConfiguration {
private SpringBootConfiguration() {
this.isShutdownHookEnabled = false;
}
} }
} }

@ -0,0 +1,54 @@
/*
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.logging.log4j2;
import java.util.Collections;
import java.util.Map;
import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
import org.apache.logging.log4j.util.BiConsumer;
import org.apache.logging.log4j.util.PropertySource;
/**
* Spring Boot {@link PropertySource} that disables Log4j2's shutdown hook.
*
* @author Andy Wilkinson
* @since 2.5.2
*/
public class SpringBootPropertySource implements PropertySource {
private static final String PREFIX = "log4j.";
private final Map<String, String> properties = Collections
.singletonMap(ShutdownCallbackRegistry.SHUTDOWN_HOOK_ENABLED, "false");
@Override
public void forEach(BiConsumer<String, String> action) {
this.properties.forEach((key, value) -> action.accept(key, value));
}
@Override
public CharSequence getNormalForm(Iterable<? extends CharSequence> tokens) {
return PREFIX + Util.joinAsCamelCase(tokens);
}
@Override
public int getPriority() {
return -200;
}
}

@ -35,6 +35,8 @@ import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig; import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.Reconfigurable; import org.apache.logging.log4j.core.config.Reconfigurable;
import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
import org.apache.logging.log4j.util.PropertiesUtil;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
@ -370,6 +372,13 @@ class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
.isEqualTo(new LoggerConfiguration("com.example.test", LogLevel.WARN, LogLevel.WARN)); .isEqualTo(new LoggerConfiguration("com.example.test", LogLevel.WARN, LogLevel.WARN));
} }
@Test
void shutdownHookIsDisabled() {
assertThat(
PropertiesUtil.getProperties().getBooleanProperty(ShutdownCallbackRegistry.SHUTDOWN_HOOK_ENABLED, true))
.isFalse();
}
private String getRelativeClasspathLocation(String fileName) { private String getRelativeClasspathLocation(String fileName) {
String defaultPath = ClassUtils.getPackageName(getClass()); String defaultPath = ClassUtils.getPackageName(getClass());
defaultPath = defaultPath.replace('.', '/'); defaultPath = defaultPath.replace('.', '/');

@ -1,42 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.logging.log4j2;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link SpringBootConfigurationFactory}.
*
* @author Andy Wilkinson
*/
class SpringBootConfigurationFactoryTests {
@Test
void producesConfigurationWithShutdownHookDisabled() throws IOException {
ConfigurationSource source = new ConfigurationSource(new ByteArrayInputStream(new byte[0]));
assertThat(new SpringBootConfigurationFactory().getConfiguration(new LoggerContext(""), source)
.isShutdownHookEnabled()).isFalse();
}
}
Loading…
Cancel
Save