#89: Log-Datei landet im MSI-Betrieb verlaesslich auf der Platte
Log4j2 referenziert nun ${sys:log.directory} mit nutzerschreibbarem
Fallback (~/pdf-umbenenner/logs). Die System-Property wird vor dem
ersten Logger-Zugriff aus der aktiven Konfigurationsdatei gesetzt
(EarlyLogDirectoryInitializer), damit Log4j2 bereits bei der
Erstinitialisierung den korrekten Pfad kennt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+7
-2
@@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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);
|
||||
|
||||
+87
@@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* Greift {@code --config <pfad>} 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;
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,13 @@
|
||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>
|
||||
</Console>
|
||||
|
||||
<!-- Rolling file appender for logs in ./logs/ directory -->
|
||||
<RollingFile name="File" fileName="logs/pdf-umbenenner.log"
|
||||
filePattern="logs/pdf-umbenenner-%d{yyyy-MM-dd}-%i.log.gz">
|
||||
<!-- Rolling file appender. Honours the system property log.directory which
|
||||
is set very early in the bootstrap from the active configuration file.
|
||||
The fallback intentionally points to a guaranteed-writable user-scoped
|
||||
directory so MSI installs (where the working directory typically lives
|
||||
under Program Files and is not user-writable) still produce log files. -->
|
||||
<RollingFile name="File" fileName="${sys:log.directory:-${sys:user.home}/pdf-umbenenner/logs}/pdf-umbenenner.log"
|
||||
filePattern="${sys:log.directory:-${sys:user.home}/pdf-umbenenner/logs}/pdf-umbenenner-%d{yyyy-MM-dd}-%i.log.gz">
|
||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
|
||||
|
||||
Reference in New Issue
Block a user