Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[issue#255] Close log4j2 loggerContext when the module is uninstalled #208

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Willhow-Gao
Copy link

@Willhow-Gao Willhow-Gao commented Nov 15, 2024

fix issue 255

Summary by CodeRabbit

  • New Features

    • Introduced a new logging event handler to manage logging context during business process stops.
    • Added a new dependency for enhanced logging capabilities.
  • Bug Fixes

    • Improved error handling in the logging context management process.

Copy link
Contributor

coderabbitai bot commented Nov 15, 2024

Walkthrough

The changes in this pull request involve modifications to the koupleless-base-plugin project, specifically the addition of a new dependency and the introduction of a new event handler. A dependency on log4j-core has been added to the pom.xml file, enabling logging capabilities. Additionally, the ServerlessRuntimeActivator class has been updated to register a new event handler, StopLoggerCxtAfterBizStopEventHandler, which manages the logging context during business stop events. This new handler is implemented in a separate file and includes methods for handling events and releasing the logging context.

Changes

File Change Summary
koupleless-base-plugin/pom.xml Added dependency: log4j-core from org.apache.logging.log4j with scope provided.
koupleless-base-plugin/src/main/java/com/alipay/sofa/koupleless/plugin/ServerlessRuntimeActivator.java Updated registerEventHandler method to register StopLoggerCxtAfterBizStopEventHandler.
koupleless-base-plugin/src/main/java/com/alipay/sofa/koupleless/plugin/manager/handler/StopLoggerCxtAfterBizStopEventHandler.java Added class StopLoggerCxtAfterBizStopEventHandler with methods for handling BeforeBizStopEvent and managing logging context.

Poem

🐇 In the code where bunnies hop,
New logs will now never stop!
With handlers swift and bright,
They’ll manage logs, day and night.
So let’s cheer for changes made,
In our plugin, joy displayed! 🎉


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (5)
koupleless-base-plugin/src/main/java/com/alipay/sofa/koupleless/plugin/ServerlessRuntimeActivator.java (1)

44-47: Consider grouping related event handlers together

The new logger context handler is placed separately from other cleanup-related handlers. Consider grouping it with other cleanup handlers for better code organization and maintainability.

-        eventAdminService.register(new StopLoggerCxtAfterBizStopEventHandler());
         eventAdminService.register(new BizUninstallEventHandler());
         eventAdminService.register(new BeforeBizStartupEventHandler());
 
         // 清理用户主动托管给 Serverless 运行时的 ExecutorService (含线程池), Timer 和 Thread.
+        eventAdminService.register(new StopLoggerCxtAfterBizStopEventHandler());
         eventAdminService.register(new ShutdownExecutorServicesOnUninstallEventHandler());
         eventAdminService.register(new CancelTimersOnUninstallEventHandler());
         eventAdminService.register(new ForceStopThreadsOnUninstallEventHandler());
koupleless-base-plugin/src/main/java/com/alipay/sofa/koupleless/plugin/manager/handler/StopLoggerCxtAfterBizStopEventHandler.java (4)

39-39: Adjust the order of modifiers for the LOGGER field

The Java convention is to declare modifiers in the order private static final rather than private final static. Changing the order improves readability and follows standard coding practices.

Apply this diff to adjust the modifiers' order:

- private final static Logger LOGGER = getLogger(StopLoggerCxtAfterBizStopEventHandler.class);
+ private static final Logger LOGGER = getLogger(StopLoggerCxtAfterBizStopEventHandler.class);

55-56: Translate code comments to English for consistency

The comments on lines 55-56 and 75 are in Chinese. To maintain consistency and improve readability for all contributors, please translate these comments to English.

Also applies to: 75-75


59-59: Consider making the LoggerContext stop timeout configurable

The timeout value of 300 milliseconds in ctx.stop(300, TimeUnit.MILLISECONDS) is hardcoded. Consider extracting this value into a constant or making it configurable to allow flexibility and easier adjustment in different environments.

Apply this diff to extract the timeout value:

+        private static final long LOGGER_CONTEXT_STOP_TIMEOUT = 300;

...

-                            boolean stop = ctx.stop(300, TimeUnit.MILLISECONDS);
+                            boolean stop = ctx.stop(LOGGER_CONTEXT_STOP_TIMEOUT, TimeUnit.MILLISECONDS);

37-80: Add unit tests for StopLoggerCxtAfterBizStopEventHandler

Since this is a new class handling critical cleanup operations, consider adding unit tests to verify its behavior under different scenarios, such as when Log4jContextFactory is present or absent, and when exceptions occur during context shutdown.

Would you like assistance in creating unit tests for this class?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between a34b4fe and 1cc207d.

📒 Files selected for processing (3)
  • koupleless-base-plugin/pom.xml (1 hunks)
  • koupleless-base-plugin/src/main/java/com/alipay/sofa/koupleless/plugin/ServerlessRuntimeActivator.java (1 hunks)
  • koupleless-base-plugin/src/main/java/com/alipay/sofa/koupleless/plugin/manager/handler/StopLoggerCxtAfterBizStopEventHandler.java (1 hunks)
🔇 Additional comments (4)
koupleless-base-plugin/src/main/java/com/alipay/sofa/koupleless/plugin/ServerlessRuntimeActivator.java (2)

44-44: Verify handler execution order for proper cleanup sequence

The new StopLoggerCxtAfterBizStopEventHandler is registered first in the sequence of handlers. Since logging might be needed during the cleanup of other resources (ExecutorServices, Timers, Threads), we should verify that the handler's priority allows logging to remain available during the cleanup process.

Let's check the priority implementation and compare it with other handlers:

✅ Verification successful

Let me analyze the verification results and generate a final response:

Logger context cleanup is properly ordered with other cleanup handlers

The StopLoggerCxtAfterBizStopEventHandler is registered to handle BeforeBizRecycleEvent, which is the same event type handled by other cleanup handlers like ShutdownExecutorServicesOnUninstallEventHandler, ForceStopThreadsOnUninstallEventHandler, and CancelTimersOnUninstallEventHandler. The logging context cleanup is safe because:

  1. The handler stops only its own logger context by matching the classloader identity
  2. The cleanup is performed using a graceful timeout (300ms)
  3. Other handlers can still log during their cleanup as they operate on their own resources independently
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check priority implementation across handlers to ensure proper ordering

# Search for getPriority implementations in all handlers
ast-grep --pattern 'class $_ implements EventHandler {
  $$$
  public int getPriority() {
    $$$
  }
  $$$
}'

# Also check for any priority-related constants or documentation
rg -i "priority|order" --type java

Length of output: 81957


44-44: Verify log4j2 context cleanup implementation

The new handler is crucial for addressing issue #255. Let's verify its implementation to ensure proper cleanup of log4j2 context.

✅ Verification successful

Based on the search results, I can see the implementation of StopLoggerCxtAfterBizStopEventHandler in the codebase. Let me analyze the log4j2 context cleanup implementation:

The handler properly implements the cleanup by:

  1. Getting the Log4j2 LoggerContext factory and checking if it's Log4jContextFactory
  2. Generating a unique context name based on the biz classloader's identity hash
  3. Getting all logger contexts from the selector
  4. Finding and stopping the matching context with a timeout of 300ms

The implementation looks correct and follows best practices for log4j2 context cleanup. Here's my final response:

Log4j2 context cleanup implementation verified successfully

The StopLoggerCxtAfterBizStopEventHandler properly handles log4j2 context cleanup by:

  • Using proper context identification based on classloader identity
  • Implementing graceful shutdown with timeout
  • Handling edge cases when context selector is not ClassLoaderContextSelector
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the implementation of StopLoggerCxtAfterBizStopEventHandler

# Search for log4j2 context cleanup implementation
ast-grep --pattern 'class StopLoggerCxtAfterBizStopEventHandler {
  $$$
  private static void releaseLog4j2LogCtx($_) {
    $$$
  }
  $$$
}'

# Check for any potential memory leak patterns in the implementation
rg -A 5 "LoggerContext|getContext|shutdown" --type java

Length of output: 81957

koupleless-base-plugin/pom.xml (1)

66-70: Verify log4j2 version for security vulnerabilities

The log4j-core dependency is added without an explicit version. While this is typically managed by the parent POM, it's crucial to ensure we're using a secure version given log4j's history of critical vulnerabilities (e.g., CVE-2021-44228).

✅ Verification successful

Let me gather more information about the actual log4j version being used.


Log4j dependency configuration is secure and well-structured

The verification shows that:

  • The project uses Spring Boot's log4j2 starter with 'provided' scope
  • A test dependency on log4j-slf4j-impl is explicitly versioned to 2.17.1, which is a secure version
  • The log4j-core dependency inherits version management from Spring Boot starter, which maintains secure versions

The current setup is appropriate because:

  • The 'provided' scope is correctly used
  • Version management is handled by Spring Boot's dependency management
  • The observed version (2.17.1) is after the critical CVE-2021-44228 patch
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check parent POM for log4j version and known vulnerabilities
echo "Checking parent POM for log4j version..."
find . -name "pom.xml" -exec grep -l "log4j.version" {} \;

# Check for security advisories
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: MAVEN, package: "org.apache.logging.log4j:log4j-core") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

Length of output: 1713


Script:

#!/bin/bash
# Check for log4j version in dependency management or properties
find . -name "pom.xml" -exec grep -A 5 -B 5 "log4j" {} \;

# Also check parent pom reference
find . -name "pom.xml" -exec grep -A 2 "<parent>" {} \;

Length of output: 3887

koupleless-base-plugin/src/main/java/com/alipay/sofa/koupleless/plugin/manager/handler/StopLoggerCxtAfterBizStopEventHandler.java (1)

58-64: Ensure matching LoggerContexts by name is reliable

Matching LoggerContexts using the context name derived from the class loader's identity hash code (ctxName) may not be unique or reliable in all scenarios. Consider verifying that this method uniquely identifies the correct LoggerContext, or explore alternative approaches, such as matching based on the class loader reference itself if possible.

Run the following script to check for potential duplicate context names:

✅ Verification successful

The LoggerContext name matching is reliable and safe

The code uses System.identityHashCode(bizClassLoader) to generate the context name, which is guaranteed to be unique for each distinct ClassLoader instance during its lifetime. This matches Log4j2's own implementation for ClassLoader-based context naming. The context cleanup is correctly scoped to the specific biz module's classloader.

Key observations:

  • The context name is derived from the classloader's identity hash code, which is unique per classloader instance
  • The cleanup only affects the specific biz module's logger context
  • The implementation aligns with Log4j2's classloader-based context isolation model
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify if multiple LoggerContexts have the same name derived from class loader identity hash code.

# Get all active LoggerContext names
ast-grep --pattern '$_ = new LoggerContext($_)' | awk -F'[(|)]' '{print $2}' | sort | uniq -c | awk '$1 > 1 {print $2}'

# Expected result: No duplicate LoggerContext names based on class loader identity hash codes.

Length of output: 121


Script:

#!/bin/bash
# Let's analyze how LoggerContext names are generated and used

# First, find where LoggerContext names are created or used
rg -l "LoggerContext" --type java

# Then look for specific patterns around context name generation and usage
rg "getName\(\)" --type java -B 2 -A 2

# Also check for any direct string comparisons with context names
rg "equals\(.*Name\)" --type java -B 2 -A 2

Length of output: 56191


Script:

#!/bin/bash
# Let's analyze how LoggerContext names are created and used in the specific file

# First, check how ctxName is generated
rg -B 5 -A 5 "ctxName" koupleless-base-plugin/src/main/java/com/alipay/sofa/koupleless/plugin/manager/handler/StopLoggerCxtAfterBizStopEventHandler.java

# Also check for any LoggerContext creation patterns
ast-grep --pattern 'new LoggerContext($_, $_)'

Length of output: 1374

Comment on lines +66 to +70
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<scope>provided</scope>
</dependency>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding log4j-api dependency

While log4j-core is added, it typically requires log4j-api as a companion dependency. Consider adding:

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <scope>provided</scope>
        </dependency>
+       <dependency>
+           <groupId>org.apache.logging.log4j</groupId>
+           <artifactId>log4j-api</artifactId>
+           <scope>provided</scope>
+       </dependency>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<scope>provided</scope>
</dependency>

Comment on lines +68 to +72
} catch (Throwable throwable) {
Biz source = event.getSource();
LOGGER.error("release {}:{}'s log4j2LogCtx failed,event id = {}", source.getBizName(),
source.getBizVersion(), source.getIdentity(), throwable);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid catching Throwable directly

Catching Throwable can mask serious errors such as OutOfMemoryError or StackOverflowError. It's advisable to catch Exception instead, unless there is a specific need to catch Throwable.

Apply this diff to change Throwable to Exception:

-        } catch (Throwable throwable) {
+        } catch (Exception exception) {
             Biz source = event.getSource();
-            LOGGER.error("release {}:{}'s log4j2LogCtx failed,event id = {}", source.getBizName(),
-                source.getBizVersion(), source.getIdentity(), throwable);
+            LOGGER.error("release {}:{}'s log4j2LogCtx failed, event id = {}", source.getBizName(),
+                source.getBizVersion(), source.getIdentity(), exception);
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (Throwable throwable) {
Biz source = event.getSource();
LOGGER.error("release {}:{}'s log4j2LogCtx failed,event id = {}", source.getBizName(),
source.getBizVersion(), source.getIdentity(), throwable);
}
} catch (Exception exception) {
Biz source = event.getSource();
LOGGER.error("release {}:{}'s log4j2LogCtx failed, event id = {}", source.getBizName(),
source.getBizVersion(), source.getIdentity(), exception);
}

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.

Log4j2 在模块卸载后未释放
1 participant