Skip to content

Commit b66509b

Browse files
committed
Merge pull request #46410 from ppkarwasz
* pr/46410: Polish "Add runtime hints for Log4j Core 2" Add runtime hints for Log4j Core 2 Closes gh-46410
2 parents 05172cf + e20e239 commit b66509b

File tree

4 files changed

+178
-11
lines changed

4 files changed

+178
-11
lines changed

core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -90,44 +90,50 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
9090

9191
private static final String OPTIONAL_PREFIX = "optional:";
9292

93-
private static final String LOG4J_BRIDGE_HANDLER = "org.apache.logging.log4j.jul.Log4jBridgeHandler";
93+
/**
94+
* JUL handler that routes messages to the Log4j API (optional dependency).
95+
*/
96+
static final String LOG4J_BRIDGE_HANDLER = "org.apache.logging.log4j.jul.Log4jBridgeHandler";
9497

95-
private static final String LOG4J_LOG_MANAGER = "org.apache.logging.log4j.jul.LogManager";
98+
/**
99+
* JUL LogManager that routes messages to the Log4j API as the backend.
100+
*/
101+
static final String LOG4J_LOG_MANAGER = "org.apache.logging.log4j.jul.LogManager";
96102

97103
/**
98104
* JSON tree parser used by Log4j 2 (optional dependency).
99105
*/
100-
private static final String JSON_TREE_PARSER_V2 = "com.fasterxml.jackson.databind.ObjectMapper";
106+
static final String JSON_TREE_PARSER_V2 = "com.fasterxml.jackson.databind.ObjectMapper";
101107

102108
/**
103109
* JSON tree parser embedded in Log4j 3.
104110
*/
105-
private static final String JSON_TREE_PARSER_V3 = "org.apache.logging.log4j.kit.json.JsonReader";
111+
static final String JSON_TREE_PARSER_V3 = "org.apache.logging.log4j.kit.json.JsonReader";
106112

107113
/**
108114
* Configuration factory for properties files (Log4j 2).
109115
*/
110-
private static final String PROPS_CONFIGURATION_FACTORY_V2 = "org.apache.logging.log4j.core.config.properties.PropertiesConfigurationFactory";
116+
static final String PROPS_CONFIGURATION_FACTORY_V2 = "org.apache.logging.log4j.core.config.properties.PropertiesConfigurationFactory";
111117

112118
/**
113119
* Configuration factory for properties files (Log4j 3, optional dependency).
114120
*/
115-
private static final String PROPS_CONFIGURATION_FACTORY_V3 = "org.apache.logging.log4j.config.properties.JavaPropsConfigurationFactory";
121+
static final String PROPS_CONFIGURATION_FACTORY_V3 = "org.apache.logging.log4j.config.properties.JavaPropsConfigurationFactory";
116122

117123
/**
118124
* YAML tree parser used by Log4j 2 (optional dependency).
119125
*/
120-
private static final String YAML_TREE_PARSER_V2 = "com.fasterxml.jackson.dataformat.yaml.YAMLMapper";
126+
static final String YAML_TREE_PARSER_V2 = "com.fasterxml.jackson.dataformat.yaml.YAMLMapper";
121127

122128
/**
123129
* Configuration factory for YAML files (Log4j 2, embedded).
124130
*/
125-
private static final String YAML_CONFIGURATION_FACTORY_V2 = "org.apache.logging.log4j.core.config.yaml.YamlConfigurationFactory";
131+
static final String YAML_CONFIGURATION_FACTORY_V2 = "org.apache.logging.log4j.core.config.yaml.YamlConfigurationFactory";
126132

127133
/**
128134
* Configuration factory for YAML files (Log4j 3, optional dependency).
129135
*/
130-
private static final String YAML_CONFIGURATION_FACTORY_V3 = "org.apache.logging.log4j.config.yaml.YamlConfigurationFactory";
136+
static final String YAML_CONFIGURATION_FACTORY_V3 = "org.apache.logging.log4j.config.yaml.YamlConfigurationFactory";
131137

132138
private static final SpringEnvironmentPropertySource propertySource = new SpringEnvironmentPropertySource();
133139

@@ -616,8 +622,10 @@ protected String getDefaultLogCorrelationPattern() {
616622
@Order(0)
617623
public static class Factory implements LoggingSystemFactory {
618624

619-
private static final boolean PRESENT = ClassUtils
620-
.isPresent("org.apache.logging.log4j.core.impl.Log4jContextFactory", Factory.class.getClassLoader());
625+
static final String LOG4J_CORE_CONTEXT_FACTORY = "org.apache.logging.log4j.core.impl.Log4jContextFactory";
626+
627+
private static final boolean PRESENT = ClassUtils.isPresent(LOG4J_CORE_CONTEXT_FACTORY,
628+
Factory.class.getClassLoader());
621629

622630
@Override
623631
public @Nullable LoggingSystem getLoggingSystem(ClassLoader classLoader) {
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2012-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.logging.log4j2;
18+
19+
import org.jspecify.annotations.Nullable;
20+
21+
import org.springframework.aot.hint.RuntimeHints;
22+
import org.springframework.aot.hint.RuntimeHintsRegistrar;
23+
import org.springframework.util.ClassUtils;
24+
25+
/**
26+
* {@link RuntimeHintsRegistrar} implementation for {@link Log4J2LoggingSystem}.
27+
*
28+
* @author Piotr P. Karwasz
29+
* @author Stephane Nicoll
30+
*/
31+
class Log4J2RuntimeHints implements RuntimeHintsRegistrar {
32+
33+
@Override
34+
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
35+
if (ClassUtils.isPresent(Log4J2LoggingSystem.Factory.LOG4J_CORE_CONTEXT_FACTORY, classLoader)) {
36+
registerLog4j2Hints(hints, classLoader);
37+
}
38+
}
39+
40+
private void registerLog4j2Hints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
41+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.Factory.LOG4J_CORE_CONTEXT_FACTORY);
42+
// Register default Log4j2 configuration files
43+
hints.resources().registerPattern("org/springframework/boot/logging/log4j2/log4j2.xml");
44+
hints.resources().registerPattern("org/springframework/boot/logging/log4j2/log4j2-file.xml");
45+
hints.resources().registerPattern("log4j2.springboot");
46+
// Declares the types that Log4j2LoggingSystem checks for existence reflectively.
47+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.JSON_TREE_PARSER_V2);
48+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.JSON_TREE_PARSER_V3);
49+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.PROPS_CONFIGURATION_FACTORY_V2);
50+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.PROPS_CONFIGURATION_FACTORY_V3);
51+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.YAML_TREE_PARSER_V2);
52+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.YAML_CONFIGURATION_FACTORY_V2);
53+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.YAML_CONFIGURATION_FACTORY_V3);
54+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.LOG4J_BRIDGE_HANDLER);
55+
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.LOG4J_LOG_MANAGER);
56+
}
57+
58+
}

core/spring-boot/src/main/resources/META-INF/spring/aot.factories

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ org.springframework.boot.context.config.ConfigDataLocationRuntimeHints,\
66
org.springframework.boot.context.config.ConfigDataPropertiesRuntimeHints,\
77
org.springframework.boot.env.PropertySourceRuntimeHints,\
88
org.springframework.boot.logging.java.JavaLoggingSystemRuntimeHints,\
9+
org.springframework.boot.logging.log4j2.Log4J2RuntimeHints,\
910
org.springframework.boot.logging.logback.LogbackRuntimeHints,\
1011
org.springframework.boot.logging.structured.ElasticCommonSchemaProperties$ElasticCommonSchemaPropertiesRuntimeHints,\
1112
org.springframework.boot.logging.structured.GraylogExtendedLogFormatProperties$GraylogExtendedLogFormatPropertiesRuntimeHints,\
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright 2012-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.logging.log4j2;
18+
19+
import java.net.URL;
20+
import java.net.URLClassLoader;
21+
import java.util.Arrays;
22+
23+
import org.apache.logging.log4j.core.config.properties.PropertiesConfigurationFactory;
24+
import org.apache.logging.log4j.core.config.yaml.YamlConfigurationFactory;
25+
import org.apache.logging.log4j.core.impl.Log4jContextFactory;
26+
import org.apache.logging.log4j.jul.Log4jBridgeHandler;
27+
import org.apache.logging.log4j.jul.LogManager;
28+
import org.junit.jupiter.api.Test;
29+
30+
import org.springframework.aot.hint.RuntimeHints;
31+
import org.springframework.aot.hint.predicate.ReflectionHintsPredicates;
32+
import org.springframework.aot.hint.predicate.ResourceHintsPredicates;
33+
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
34+
35+
import static org.assertj.core.api.Assertions.assertThat;
36+
37+
/**
38+
* Tests for {@link Log4J2RuntimeHints}.
39+
*
40+
* @author Piotr P. Karwasz
41+
* @author Stephane Nicoll
42+
*/
43+
class Log4J2RuntimeHintsTests {
44+
45+
private static final ReflectionHintsPredicates reflectionHints = RuntimeHintsPredicates.reflection();
46+
47+
private static final ResourceHintsPredicates resourceHints = RuntimeHintsPredicates.resource();
48+
49+
@Test
50+
void registersHintsForTypesCheckedByLog4J2LoggingSystem() {
51+
RuntimeHints runtimeHints = registerHints();
52+
assertThat(reflectionHints.onType(Log4jContextFactory.class)).accepts(runtimeHints);
53+
assertThat(reflectionHints.onType(Log4jBridgeHandler.class)).accepts(runtimeHints);
54+
assertThat(reflectionHints.onType(LogManager.class)).accepts(runtimeHints);
55+
assertThat(reflectionHints.onType(PropertiesConfigurationFactory.class)).accepts(runtimeHints);
56+
assertThat(reflectionHints.onType(YamlConfigurationFactory.class)).accepts(runtimeHints);
57+
}
58+
59+
@Test
60+
void registersHintsForLog4j2DefaultConfigurationFiles() {
61+
RuntimeHints runtimeHints = registerHints();
62+
assertThat(resourceHints.forResource("org/springframework/boot/logging/log4j2/log4j2.xml"))
63+
.accepts(runtimeHints);
64+
assertThat(resourceHints.forResource("org/springframework/boot/logging/log4j2/log4j2-file.xml"))
65+
.accepts(runtimeHints);
66+
}
67+
68+
@Test
69+
void doesNotRegisterHintsWhenLog4jCoreIsNotAvailable() {
70+
RuntimeHints runtimeHints = new RuntimeHints();
71+
new Log4J2RuntimeHints().registerHints(runtimeHints, new HidePackagesClassLoader("org.apache.logging.log4j"));
72+
assertThat(runtimeHints.reflection().typeHints()).isEmpty();
73+
}
74+
75+
private RuntimeHints registerHints() {
76+
RuntimeHints hints = new RuntimeHints();
77+
new Log4J2RuntimeHints().registerHints(hints, getClass().getClassLoader());
78+
return hints;
79+
}
80+
81+
static final class HidePackagesClassLoader extends URLClassLoader {
82+
83+
private final String[] hiddenPackages;
84+
85+
HidePackagesClassLoader(String... hiddenPackages) {
86+
super(new URL[0], HidePackagesClassLoader.class.getClassLoader());
87+
this.hiddenPackages = hiddenPackages;
88+
}
89+
90+
@Override
91+
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
92+
if (Arrays.stream(this.hiddenPackages).anyMatch(name::startsWith)) {
93+
throw new ClassNotFoundException();
94+
}
95+
return super.loadClass(name, resolve);
96+
}
97+
98+
}
99+
100+
}

0 commit comments

Comments
 (0)