如何结合Lombok以编程方式设置Log4j属性
Great question! Configuring Log4j to use a platform-specific app data directory is a common requirement for desktop apps, and JavaFX's initialization phase is the perfect place to set this up. Here's a step-by-step solution that builds on your existing ConfigurationFactory approach:
Step 1: Resolve the Platform-Specific App Data Directory
First, we need a reliable way to get the correct app data path across Windows, macOS, and Linux. Here's a helper method to handle cross-platform paths:
private static File getAppLogDirectory(String appName, String appVersion) { String os = System.getProperty("os.name").toLowerCase(); File appDataDir; if (os.contains("win")) { // Windows: %APPDATA%/<app>/<version> appDataDir = new File(System.getenv("APPDATA"), appName + File.separator + appVersion); } else if (os.contains("mac")) { // macOS: ~/Library/Application Support/<app>/<version> appDataDir = new File(System.getProperty("user.home"), "Library" + File.separator + "Application Support" + File.separator + appName + File.separator + appVersion); } else { // Linux/Unix: ~/.config/<app>/<version> appDataDir = new File(System.getProperty("user.home"), ".config" + File.separator + appName + File.separator + appVersion); } // Create directory structure if it doesn't exist if (!appDataDir.exists()) { appDataDir.mkdirs(); } return appDataDir; }
Step 2: Implement the Custom Log4j ConfigurationFactory
Now integrate this directory path into your ConfigurationFactory to set up a file appender. This example includes both a console appender and a rolling file appender (adjust log levels and patterns to your needs):
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.appender.ConsoleAppender; import org.apache.logging.log4j.core.appender.RollingFileAppender; import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy; import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.ConfigurationFactory; import org.apache.logging.log4j.core.config.ConfigurationSource; import org.apache.logging.log4j.core.config.Order; import org.apache.logging.log4j.core.config.builder.api.*; import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; import java.io.File; @Order(1) // Prioritize this factory over default Log4j configurations public class Log4jConfigurationFactory extends ConfigurationFactory { private static final String APP_NAME = "YourAppName"; private static final String APP_VERSION = "1.0.0"; private static final String LOG_FILE_NAME = "app.log"; @Override protected String[] getSupportedTypes() { return new String[]{"*"}; // Support all configuration types } @Override public Configuration getConfiguration(LoggerContext loggerContext, ConfigurationSource source) { return getConfiguration(loggerContext, source.toString(), null); } @Override public Configuration getConfiguration(LoggerContext loggerContext, String name, URI configLocation) { // Build configuration programmatically ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder(); // Set root log level builder.setStatusLevel(Level.INFO); builder.setConfigurationName("CustomAppConfig"); // Create pattern layout for log messages LayoutComponentBuilder layoutBuilder = builder.newLayout("PatternLayout") .addAttribute("pattern", "%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"); // Add console appender AppenderComponentBuilder consoleAppender = builder.newAppender("Console", "Console") .add(layoutBuilder); builder.add(consoleAppender); // Get the app log directory and log file path File logDir = getAppLogDirectory(APP_NAME, APP_VERSION); File logFile = new File(logDir, LOG_FILE_NAME); // Add rolling file appender (rolls daily or at 10MB) AppenderComponentBuilder fileAppender = builder.newAppender("File", "RollingFile") .addAttribute("fileName", logFile.getAbsolutePath()) .addAttribute("filePattern", logDir.getAbsolutePath() + File.separator + "app-%d{yyyy-MM-dd}-%i.log.gz") .add(layoutBuilder); // Configure rolling policies ComponentBuilder triggeringPolicies = builder.newComponent("Policies") .addComponent(builder.newComponent("TimeBasedTriggeringPolicy") .addAttribute("interval", 1) .addAttribute("modulate", true)) .addComponent(builder.newComponent("SizeBasedTriggeringPolicy") .addAttribute("size", "10MB")); fileAppender.add(triggeringPolicies); builder.add(fileAppender); // Attach appenders to root logger builder.add(builder.newRootLogger(Level.INFO) .add(builder.newAppenderRef("Console")) .add(builder.newAppenderRef("File"))); return builder.build(); } private static File getAppLogDirectory(String appName, String appVersion) { // Reuse the cross-platform directory helper method here String os = System.getProperty("os.name").toLowerCase(); File appDataDir; if (os.contains("win")) { appDataDir = new File(System.getenv("APPDATA"), appName + File.separator + appVersion); } else if (os.contains("mac")) { appDataDir = new File(System.getProperty("user.home"), "Library" + File.separator + "Application Support" + File.separator + appName + File.separator + appVersion); } else { appDataDir = new File(System.getProperty("user.home"), ".config" + File.separator + appName + File.separator + appVersion); } if (!appDataDir.exists()) { appDataDir.mkdirs(); } return appDataDir; } }
Step 3: Register the Factory in JavaFX Initialization
To ensure Log4j uses your custom configuration, register the factory before any logger is initialized. The init() method of your JavaFX app is ideal for this:
import javafx.application.Application; import javafx.stage.Stage; import org.apache.logging.log4j.core.config.ConfigurationFactory; public class YourJavaFXApp extends Application { @Override public void init() throws Exception { super.init(); // Register our custom Log4j configuration factory ConfigurationFactory.setConfigurationFactory(new Log4jConfigurationFactory()); } @Override public void start(Stage primaryStage) throws Exception { // Your app UI initialization logic here primaryStage.setTitle("Your App"); // ... rest of your application code } public static void main(String[] args) { launch(args); } }
Key Notes:
- Cross-Platform Support: The directory helper method uses standard OS-specific app data locations, so your app behaves consistently across Windows, macOS, and Linux.
- Rolling Appender: The example uses a rolling file appender that creates compressed archives of old logs, preventing log files from growing indefinitely. Adjust the
sizeandintervalattributes to match your needs. - Priority: The
@Order(1)annotation ensures your factory takes precedence over default Log4j configurations. - Directory Creation: The
mkdirs()call ensures the entire directory path is created if it doesn't exist, so you don't have to handle missing folders manually.
Just replace YourAppName and 1.0.0 with your actual app details, and tweak the log pattern/levels to fit your application's requirements. This setup will automatically create the log file in the correct app data directory when your JavaFX app starts.
内容的提问来源于stack exchange,提问作者Eray Erdin

