diff --git a/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/PdfUmbenennerApplication.java b/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/PdfUmbenennerApplication.java index e2da4d9..9977157 100644 --- a/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/PdfUmbenennerApplication.java +++ b/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/PdfUmbenennerApplication.java @@ -4,6 +4,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import de.gecheckt.pdf.umbenenner.bootstrap.startup.CliArgumentParser; +import de.gecheckt.pdf.umbenenner.bootstrap.startup.EarlyLogDirectoryInitializer; import de.gecheckt.pdf.umbenenner.bootstrap.startup.StartupArguments; import de.gecheckt.pdf.umbenenner.bootstrap.startup.StartupArgumentsParseResult; @@ -28,18 +29,22 @@ import de.gecheckt.pdf.umbenenner.bootstrap.startup.StartupArgumentsParseResult; */ public class PdfUmbenennerApplication { - private static final Logger LOG = LogManager.getLogger(PdfUmbenennerApplication.class); - /** * Application entry point. *

* Parses the command-line arguments and delegates to {@link BootstrapRunner}. * If the arguments cannot be parsed, an error is logged and the process exits * with code 1 before any further initialisation takes place. + *

+ * Vor jeder Logger-Nutzung wird {@link EarlyLogDirectoryInitializer} aufgerufen, + * damit die Property {@code log.directory} aus der aktiven Konfigurationsdatei + * bereits vor der einmaligen Log4j2-Initialisierung gesetzt ist. * * @param args command-line arguments; see class JavaDoc for supported options */ public static void main(String[] args) { + EarlyLogDirectoryInitializer.applyFromArgs(args); + Logger LOG = LogManager.getLogger(PdfUmbenennerApplication.class); LOG.info("Starting PDF Umbenenner application..."); try { StartupArgumentsParseResult parseResult = new CliArgumentParser().parse(args); diff --git a/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/startup/EarlyLogDirectoryInitializer.java b/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/startup/EarlyLogDirectoryInitializer.java new file mode 100644 index 0000000..f4f5475 --- /dev/null +++ b/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/startup/EarlyLogDirectoryInitializer.java @@ -0,0 +1,87 @@ +package de.gecheckt.pdf.umbenenner.bootstrap.startup; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Properties; + +/** + * Liest die Log-Verzeichnis-Angabe so früh wie möglich aus der aktiven Konfigurationsdatei + * und setzt sie als System-Property {@code log.directory}, bevor Log4j2 zum ersten Mal + * initialisiert wird. + *

+ * Hintergrund: {@code log4j2.xml} referenziert {@code ${sys:log.directory}} für den + * Pfad der Rolling-File-Datei. Da Log4j2 sich beim ersten {@code LogManager}-Aufruf + * einmalig konfiguriert, muss die System-Property bereits vorher gesetzt sein. Greift + * die Property nicht, fällt {@code log4j2.xml} auf ein nutzerschreibbares + * Default-Verzeichnis zurück, damit auch im MSI-Betrieb (Arbeitsverzeichnis unter + * {@code Program Files}, typischerweise nicht beschreibbar) eine Log-Datei entsteht. + *

+ * Diese Klasse vermeidet bewusst jede Logger-Nutzung und schluckt sämtliche Fehler: + * Sie soll niemals den Programmstart verhindern, sondern lediglich einen frühen + * Best-Effort-Hinweis an Log4j2 liefern. + */ +public final class EarlyLogDirectoryInitializer { + + private static final String SYSTEM_PROPERTY_KEY = "log.directory"; + private static final String CONFIG_OPTION = "--config"; + private static final Path DEFAULT_CONFIG_PATH = Paths.get("config/application.properties"); + private static final String CONFIG_PROPERTY_KEY = "log.directory"; + + private EarlyLogDirectoryInitializer() { + // utility + } + + /** + * Versucht, aus der aktiven Konfigurationsdatei den Wert von {@code log.directory} + * zu lesen, und setzt ihn als System-Property, sofern er ein nicht-leerer String ist. + *

+ * Greift {@code --config } auf, ansonsten {@code config/application.properties} + * relativ zum Arbeitsverzeichnis. Ist kein Wert ableitbar, bleibt die System-Property + * unverändert; in diesem Fall greift der in {@code log4j2.xml} hinterlegte Fallback. + * + * @param args Kommandozeilenargumente, dürfen {@code null} sein + */ + public static void applyFromArgs(String[] args) { + try { + if (System.getProperty(SYSTEM_PROPERTY_KEY) != null + && !System.getProperty(SYSTEM_PROPERTY_KEY).isBlank()) { + return; + } + Path configPath = resolveConfigPath(args); + if (configPath == null || !Files.isRegularFile(configPath)) { + return; + } + Properties properties = new Properties(); + try (InputStream in = Files.newInputStream(configPath)) { + properties.load(in); + } + String value = properties.getProperty(CONFIG_PROPERTY_KEY); + if (value != null && !value.isBlank()) { + System.setProperty(SYSTEM_PROPERTY_KEY, value.trim()); + } + } catch (IOException | RuntimeException ignored) { + // bewusst still: Log4j2-Fallback aus log4j2.xml übernimmt ansonsten + } + } + + private static Path resolveConfigPath(String[] args) { + if (args != null) { + for (int i = 0; i < args.length - 1; i++) { + if (CONFIG_OPTION.equals(args[i])) { + String raw = args[i + 1]; + if (raw != null && !raw.isBlank()) { + try { + return Paths.get(raw); + } catch (RuntimeException ignored) { + return null; + } + } + } + } + } + return DEFAULT_CONFIG_PATH; + } +} diff --git a/pdf-umbenenner-bootstrap/src/main/resources/log4j2.xml b/pdf-umbenenner-bootstrap/src/main/resources/log4j2.xml index 360ac74..455b368 100644 --- a/pdf-umbenenner-bootstrap/src/main/resources/log4j2.xml +++ b/pdf-umbenenner-bootstrap/src/main/resources/log4j2.xml @@ -6,9 +6,13 @@ - - + +