Skip to content

Conversation

@JGoP-L
Copy link
Contributor

@JGoP-L JGoP-L commented Dec 11, 2025

[ISSUE #13940] Eliminate ClassUnload during config reload using Configuration.initialize()

What is the purpose of the change

This PR fixes issue #13940 where Log4j2 configuration reload causes ClassUnload in middleware environments (Dubbo, Spring Cloud, etc.). The root cause is using Configuration.start() which reinitializes Log4j2 plugins, triggering class unloading of Nacos classes. By using Configuration.initialize() instead, we avoid plugin reinitialization while properly configuring Log4j2.

Brief changelog

  • Add NacosLog4j2Configurator class that uses Configuration.initialize() instead of Configuration.start() to avoid plugin reinitialization and ClassUnload issues
  • Refactor Log4J2NacosLoggingAdapter to delegate configuration loading to the new NacosLog4j2Configurator
  • Add thread-safe double-checked locking pattern for configuration loading
  • Implement additive configuration merging to preserve user's logging setup
  • Update test implementation for v2.x Mockito compatibility
  • Add comprehensive javadoc explaining the fix and why it's necessary

Verifying this change

Test Results

[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS

Files Changed

  1. logger-adapter-impl/log4j2-adapter/src/main/java/com/alibaba/nacos/logger/adapter/log4j2/NacosLog4j2Configurator.java (NEW - 109 lines)

  2. logger-adapter-impl/log4j2-adapter/src/main/java/com/alibaba/nacos/logger/adapter/log4j2/Log4J2NacosLoggingAdapter.java

    • Delegates to NacosLog4j2Configurator
    • Adds thread-safe configuration loading with double-checked locking
    • Removes old problematic configuration.start() call
  3. logger-adapter-impl/log4j2-adapter/src/main/java/com/alibaba/nacos/logger/adapter/log4j2/Log4j2NacosLoggingPropertiesHolder.java

    • Adds public getter method for logging properties
  4. logger-adapter-impl/log4j2-adapter/src/test/java/com/alibaba/nacos/logger/adapter/log4j2/Log4J2NacosLoggingAdapterTest.java

    • Removes deprecated test method
    • Adapts for v2.x Mockito compatibility

Related Issues

Fixes #13940
Based on: #14000 (commit 90747d4)

Notes for Reviewers

This PR directly applies the official fix from alibaba/nacos PR #14000 to v2.x-develop branch. The implementation:

…lize()

Fixes alibaba#13940 - Apply official PR alibaba#14000 fix to v2.x-develop branch

This commit applies the official fix from alibaba/nacos PR alibaba#14000 (commit 90747d4)
to resolve ClassUnload issues during Log4j2 configuration reload.

Key changes:
1. Add NacosLog4j2Configurator class using Configuration.initialize()
   instead of Configuration.start() to avoid plugin reinitialization
2. Refactor Log4J2NacosLoggingAdapter to delegate to new configurator
3. Remove deprecated helper methods and simplify test implementation
4. Add thread-safe double-checked locking pattern
5. Additively merge Nacos config without replacing user's logging setup

The fix follows framework-compliant pattern similar to Logback adapter.

Adapted from: alibaba#14000
Original commit: 90747d4
Copilot AI review requested due to automatic review settings December 11, 2025 11:40
@github-actions
Copy link

Thanks for your this PR. 🙏
Please check again for your PR changes whether contains any usage/api/configuration change such as Add new API , Add new configuration, Change default value of configuration.
If so, please add or update documents(markdown type) in docs/next/ for repository nacos-group/nacos-group.github.io


感谢您提交的PR。 🙏
请再次查看您的PR内容,确认是否包含任何使用方式/API/配置参数的变更,如:新增API新增配置参数修改默认配置等操作。
如果是,请确保在提交之前,在仓库nacos-group/nacos-group.github.io中的docs/next/目录下添加或更新文档(markdown格式)。

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes issue #13940 where Log4j2 configuration reload causes ClassUnload in middleware environments (Dubbo, Spring Cloud, etc.). The root cause is that calling Configuration.start() reinitializes Log4j2 plugins, triggering class unloading of Nacos classes. The fix uses Configuration.initialize() instead, which sets up the configuration without plugin reinitialization. This implementation follows the same additive configuration pattern as the existing Logback adapter (NacosLogbackConfiguratorAdapterV1).

Key Changes:

  • Introduced NacosLog4j2Configurator to handle Log4j2 configuration using Configuration.initialize() instead of Configuration.start()
  • Refactored Log4J2NacosLoggingAdapter to delegate to the new configurator with thread-safe double-checked locking
  • Updated tests to remove Mockito dependencies and verify functionality works with the new implementation

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
NacosLog4j2Configurator.java New configurator class that uses Configuration.initialize() to avoid ClassUnload issue and additively merges Nacos logging configuration
Log4J2NacosLoggingAdapter.java Refactored to use NacosLog4j2Configurator with thread-safe double-checked locking pattern for configuration loading
Log4j2NacosLoggingPropertiesHolder.java Added public getter method for properties (currently unused)
Log4J2NacosLoggingAdapterTest.java Removed Mockito-based tests and verification calls to align with v2.x testing approach

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +42 to +44
public static NacosLoggingProperties getProperties() {
return INSTANCE.properties;
}
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The newly added getProperties() method is not used anywhere in the codebase. If it's intended for future use or as a public API, consider documenting its purpose. If it's not needed, consider removing it to avoid maintaining unused code.

Suggested change
public static NacosLoggingProperties getProperties() {
return INSTANCE.properties;
}

Copilot uses AI. Check for mistakes.
Comment on lines +47 to +109
public class NacosLog4j2Configurator {

private static final String NACOS_LOGGER_PREFIX = "com.alibaba.nacos";

/**
* Configure LoggerContext by loading Nacos configuration from URI.
* This method additively merges Nacos appenders and loggers into the existing configuration.
*
* @param loggerContext The LoggerContext to configure
* @param configLocation URI of the Nacos Log4j2 configuration file
* @throws IOException if configuration file cannot be read
*/
public void configure(LoggerContext loggerContext, URI configLocation) throws IOException {
Configuration nacosConfig = loadConfiguration(loggerContext, configLocation);

// Key fix for issue #13940: Use initialize() instead of start()
// initialize() sets up the configuration without triggering plugin reinitialization
nacosConfig.initialize();

// Get the current active configuration
Configuration currentConfig = loggerContext.getConfiguration();

// Additively merge Nacos appenders (non-invasive approach for middleware)
// Note: Appenders are started individually and added to currentConfig
// They are NOT removed from nacosConfig to avoid lifecycle issues
nacosConfig.getAppenders().values().forEach(appender -> {
if (!appender.isStarted()) {
appender.start();
}
currentConfig.addAppender(appender);
});

// Add only Nacos-specific loggers to avoid interfering with user configuration
nacosConfig.getLoggers().entrySet().stream()
.filter(entry -> entry.getKey().startsWith(NACOS_LOGGER_PREFIX))
.forEach(entry -> currentConfig.addLogger(entry.getKey(), entry.getValue()));

// Apply the merged configuration
loggerContext.updateLoggers();

// Important: Do NOT call nacosConfig.stop() here!
// The appenders and loggers have been transferred to currentConfig.
// Calling stop() would shut down the appenders that are now owned by currentConfig.
// nacosConfig will be garbage collected naturally, and since we only called initialize()
// (not start()), there are no active background threads or resources to clean up.
}

/**
* Load Log4j2 configuration from URI using ConfigurationFactory.
* This is the standard Log4j2 way to parse configuration files.
*
* @param ctx LoggerContext
* @param configLocation URI of configuration file
* @return Parsed Configuration object
* @throws IOException if configuration cannot be loaded
*/
private Configuration loadConfiguration(LoggerContext ctx, URI configLocation) throws IOException {
try (InputStream stream = configLocation.toURL().openStream()) {
ConfigurationSource source = new ConfigurationSource(stream, configLocation.toURL());
return ConfigurationFactory.getInstance().getConfiguration(ctx, source);
}
}
}
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new NacosLog4j2Configurator class introduced in this PR lacks direct unit tests. While it's indirectly tested through Log4J2NacosLoggingAdapterTest, consider adding dedicated unit tests to verify its behavior in isolation, particularly the additive configuration merging logic and the use of Configuration.initialize() vs Configuration.start().

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant