Browse Source

Polish "Add runtime hints for Log4j Core 2"

See gh-46410
pull/47436/head
Stéphane Nicoll 5 months ago
parent
commit
e20e2393c9
  1. 3
      core/spring-boot/build.gradle
  2. 43
      core/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2RuntimeHints.java
  3. 73
      core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2RuntimeHintsTests.java

3
core/spring-boot/build.gradle

@ -71,9 +71,6 @@ dependencies {
testImplementation("org.hibernate.validator:hibernate-validator") testImplementation("org.hibernate.validator:hibernate-validator")
testImplementation("org.jboss.logging:jboss-logging") testImplementation("org.jboss.logging:jboss-logging")
testImplementation("org.springframework.data:spring-data-r2dbc") testImplementation("org.springframework.data:spring-data-r2dbc")
// Used in Log4J2RuntimeHintsTests
testRuntimeOnly("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml")
} }
def syncJavaTemplates = tasks.register("syncJavaTemplates", Sync) { def syncJavaTemplates = tasks.register("syncJavaTemplates", Sync) {

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2025-present the original author or authors. * Copyright 2012-present the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -26,42 +26,33 @@ import org.springframework.util.ClassUtils;
* {@link RuntimeHintsRegistrar} implementation for {@link Log4J2LoggingSystem}. * {@link RuntimeHintsRegistrar} implementation for {@link Log4J2LoggingSystem}.
* *
* @author Piotr P. Karwasz * @author Piotr P. Karwasz
* @author Stephane Nicoll
*/ */
class Log4J2RuntimeHints implements RuntimeHintsRegistrar { class Log4J2RuntimeHints implements RuntimeHintsRegistrar {
@Override @Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
if (!ClassUtils.isPresent(Log4J2LoggingSystem.Factory.LOG4J_CORE_CONTEXT_FACTORY, classLoader)) { if (ClassUtils.isPresent(Log4J2LoggingSystem.Factory.LOG4J_CORE_CONTEXT_FACTORY, classLoader)) {
return; registerLog4j2Hints(hints, classLoader);
} }
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.Factory.LOG4J_CORE_CONTEXT_FACTORY); }
private void registerLog4j2Hints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.Factory.LOG4J_CORE_CONTEXT_FACTORY);
// Register default Log4j2 configuration files // Register default Log4j2 configuration files
hints.resources().registerPattern("org/springframework/boot/logging/log4j2/log4j2.xml"); hints.resources().registerPattern("org/springframework/boot/logging/log4j2/log4j2.xml");
hints.resources().registerPattern("org/springframework/boot/logging/log4j2/log4j2-file.xml"); hints.resources().registerPattern("org/springframework/boot/logging/log4j2/log4j2-file.xml");
hints.resources().registerPattern("log4j2.springboot"); hints.resources().registerPattern("log4j2.springboot");
// Declares the types that Log4j2LoggingSystem checks for existence reflectively. // Declares the types that Log4j2LoggingSystem checks for existence reflectively.
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.JSON_TREE_PARSER_V2); hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.JSON_TREE_PARSER_V2);
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.JSON_TREE_PARSER_V3); hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.JSON_TREE_PARSER_V3);
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.PROPS_CONFIGURATION_FACTORY_V2); hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.PROPS_CONFIGURATION_FACTORY_V2);
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.PROPS_CONFIGURATION_FACTORY_V3); hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.PROPS_CONFIGURATION_FACTORY_V3);
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.YAML_TREE_PARSER_V2); hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.YAML_TREE_PARSER_V2);
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.YAML_CONFIGURATION_FACTORY_V2); hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.YAML_CONFIGURATION_FACTORY_V2);
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.YAML_CONFIGURATION_FACTORY_V3); hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.YAML_CONFIGURATION_FACTORY_V3);
// Register JUL to Log4j 2 bridge handler hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.LOG4J_BRIDGE_HANDLER);
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.LOG4J_BRIDGE_HANDLER); hints.reflection().registerTypeIfPresent(classLoader, Log4J2LoggingSystem.LOG4J_LOG_MANAGER);
registerTypeForReachability(hints, classLoader, Log4J2LoggingSystem.LOG4J_LOG_MANAGER);
// Don't need to register the custom Log4j 2 plugins,
// since they will be registered by the Log4j 2 `GraalvmPluginProcessor`.
}
/**
* Registers the type to prevent GraalVM from removing it during the native build.
* @param hints the runtime hints to register with
* @param classLoader the class loader to use for type resolution
* @param typeName the name of the type to register
*/
private void registerTypeForReachability(RuntimeHints hints, @Nullable ClassLoader classLoader, String typeName) {
hints.reflection().registerTypeIfPresent(classLoader, typeName);
} }
} }

73
core/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2RuntimeHintsTests.java

@ -16,14 +16,21 @@
package org.springframework.boot.logging.log4j2; package org.springframework.boot.logging.log4j2;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import org.apache.logging.log4j.core.config.properties.PropertiesConfigurationFactory;
import org.apache.logging.log4j.core.config.yaml.YamlConfigurationFactory;
import org.apache.logging.log4j.core.impl.Log4jContextFactory; import org.apache.logging.log4j.core.impl.Log4jContextFactory;
import org.apache.logging.log4j.jul.Log4jBridgeHandler; import org.apache.logging.log4j.jul.Log4jBridgeHandler;
import org.apache.logging.log4j.jul.LogManager; import org.apache.logging.log4j.jul.LogManager;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.ReflectionHints;
import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.TypeReference; import org.springframework.aot.hint.predicate.ReflectionHintsPredicates;
import org.springframework.aot.hint.predicate.ResourceHintsPredicates;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -31,43 +38,63 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link Log4J2RuntimeHints}. * Tests for {@link Log4J2RuntimeHints}.
* *
* @author Piotr P. Karwasz * @author Piotr P. Karwasz
* @author Stephane Nicoll
*/ */
class Log4J2RuntimeHintsTests { class Log4J2RuntimeHintsTests {
private static final ReflectionHintsPredicates reflectionHints = RuntimeHintsPredicates.reflection();
private static final ResourceHintsPredicates resourceHints = RuntimeHintsPredicates.resource();
@Test @Test
void registersHintsForTypesCheckedByLog4J2LoggingSystem() { void registersHintsForTypesCheckedByLog4J2LoggingSystem() {
ReflectionHints reflection = registerHints(); RuntimeHints runtimeHints = registerHints();
// Once Log4j Core is reachable, GraalVM will automatically assertThat(reflectionHints.onType(Log4jContextFactory.class)).accepts(runtimeHints);
// add reachability metadata embedded in the Log4j Core jar and extensions. assertThat(reflectionHints.onType(Log4jBridgeHandler.class)).accepts(runtimeHints);
assertThat(reflection.getTypeHint(Log4jContextFactory.class)).isNotNull(); assertThat(reflectionHints.onType(LogManager.class)).accepts(runtimeHints);
assertThat(reflection.getTypeHint(Log4jBridgeHandler.class)).isNotNull(); assertThat(reflectionHints.onType(PropertiesConfigurationFactory.class)).accepts(runtimeHints);
assertThat(reflection.getTypeHint(LogManager.class)).isNotNull(); assertThat(reflectionHints.onType(YamlConfigurationFactory.class)).accepts(runtimeHints);
} }
/**
*
*/
@Test @Test
void registersHintsForConfigurationFileParsers() { void registersHintsForLog4j2DefaultConfigurationFiles() {
ReflectionHints reflection = registerHints(); RuntimeHints runtimeHints = registerHints();
// JSON assertThat(resourceHints.forResource("org/springframework/boot/logging/log4j2/log4j2.xml"))
assertThat(reflection.getTypeHint(TypeReference.of("com.fasterxml.jackson.databind.ObjectMapper"))).isNotNull(); .accepts(runtimeHints);
// YAML assertThat(resourceHints.forResource("org/springframework/boot/logging/log4j2/log4j2-file.xml"))
assertThat(reflection.getTypeHint(TypeReference.of("com.fasterxml.jackson.dataformat.yaml.YAMLMapper"))) .accepts(runtimeHints);
.isNotNull();
} }
@Test @Test
void doesNotRegisterHintsWhenLog4jCoreIsNotAvailable() { void doesNotRegisterHintsWhenLog4jCoreIsNotAvailable() {
RuntimeHints hints = new RuntimeHints(); RuntimeHints runtimeHints = new RuntimeHints();
new Log4J2RuntimeHints().registerHints(hints, ClassLoader.getPlatformClassLoader()); new Log4J2RuntimeHints().registerHints(runtimeHints, new HidePackagesClassLoader("org.apache.logging.log4j"));
assertThat(hints.reflection().typeHints()).isEmpty(); assertThat(runtimeHints.reflection().typeHints()).isEmpty();
} }
private ReflectionHints registerHints() { private RuntimeHints registerHints() {
RuntimeHints hints = new RuntimeHints(); RuntimeHints hints = new RuntimeHints();
new Log4J2RuntimeHints().registerHints(hints, getClass().getClassLoader()); new Log4J2RuntimeHints().registerHints(hints, getClass().getClassLoader());
return hints.reflection(); return hints;
}
static final class HidePackagesClassLoader extends URLClassLoader {
private final String[] hiddenPackages;
HidePackagesClassLoader(String... hiddenPackages) {
super(new URL[0], HidePackagesClassLoader.class.getClassLoader());
this.hiddenPackages = hiddenPackages;
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (Arrays.stream(this.hiddenPackages).anyMatch(name::startsWith)) {
throw new ClassNotFoundException();
}
return super.loadClass(name, resolve);
}
} }
} }

Loading…
Cancel
Save