#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 org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import de.gecheckt.pdf.umbenenner.bootstrap.startup.CliArgumentParser;
|
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.StartupArguments;
|
||||||
import de.gecheckt.pdf.umbenenner.bootstrap.startup.StartupArgumentsParseResult;
|
import de.gecheckt.pdf.umbenenner.bootstrap.startup.StartupArgumentsParseResult;
|
||||||
|
|
||||||
@@ -28,18 +29,22 @@ import de.gecheckt.pdf.umbenenner.bootstrap.startup.StartupArgumentsParseResult;
|
|||||||
*/
|
*/
|
||||||
public class PdfUmbenennerApplication {
|
public class PdfUmbenennerApplication {
|
||||||
|
|
||||||
private static final Logger LOG = LogManager.getLogger(PdfUmbenennerApplication.class);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Application entry point.
|
* Application entry point.
|
||||||
* <p>
|
* <p>
|
||||||
* Parses the command-line arguments and delegates to {@link BootstrapRunner}.
|
* Parses the command-line arguments and delegates to {@link BootstrapRunner}.
|
||||||
* If the arguments cannot be parsed, an error is logged and the process exits
|
* If the arguments cannot be parsed, an error is logged and the process exits
|
||||||
* with code 1 before any further initialisation takes place.
|
* 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
|
* @param args command-line arguments; see class JavaDoc for supported options
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
EarlyLogDirectoryInitializer.applyFromArgs(args);
|
||||||
|
Logger LOG = LogManager.getLogger(PdfUmbenennerApplication.class);
|
||||||
LOG.info("Starting PDF Umbenenner application...");
|
LOG.info("Starting PDF Umbenenner application...");
|
||||||
try {
|
try {
|
||||||
StartupArgumentsParseResult parseResult = new CliArgumentParser().parse(args);
|
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"/>
|
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>
|
||||||
</Console>
|
</Console>
|
||||||
|
|
||||||
<!-- Rolling file appender for logs in ./logs/ directory -->
|
<!-- Rolling file appender. Honours the system property log.directory which
|
||||||
<RollingFile name="File" fileName="logs/pdf-umbenenner.log"
|
is set very early in the bootstrap from the active configuration file.
|
||||||
filePattern="logs/pdf-umbenenner-%d{yyyy-MM-dd}-%i.log.gz">
|
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"/>
|
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>
|
||||||
<Policies>
|
<Policies>
|
||||||
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
|
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user