From 0138b5924ff14ef709398bf878dbb9bf2a0b7c33 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Wed, 23 Sep 2026 13:05:17 +0000
Subject: [PATCH] [#1030] Keep the setup log out of the way of start-ds, and say when it is gone (#1032)
---
opendj-server-legacy/src/main/java/org/opends/quicksetup/TempLogFile.java | 102 +++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 98 insertions(+), 4 deletions(-)
diff --git a/opendj-server-legacy/src/main/java/org/opends/quicksetup/TempLogFile.java b/opendj-server-legacy/src/main/java/org/opends/quicksetup/TempLogFile.java
index ccb1c9e..6f193b8 100644
--- a/opendj-server-legacy/src/main/java/org/opends/quicksetup/TempLogFile.java
+++ b/opendj-server-legacy/src/main/java/org/opends/quicksetup/TempLogFile.java
@@ -20,6 +20,7 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.Date;
import java.text.DateFormat;
@@ -51,9 +52,51 @@
*/
public static TempLogFile newTempLogFile(final String prefix)
{
+ return newTempLogFile(prefix, null);
+ }
+
+ /**
+ * Creates a new temporary log file in the given directory.
+ * <p>
+ * The directory is created if it does not exist yet. When it is {@code null} or cannot be
+ * used, the log file goes to the OS temporary directory instead, as with
+ * {@link #newTempLogFile(String)}. The name of the file follows the pattern
+ * prefix-[RANDOM_NUMBER_STRING].log either way.
+ *
+ * @param prefix
+ * log file prefix to which log messages will be written.
+ * @param directory
+ * the directory to create the log file in, or {@code null} for the OS temporary
+ * directory.
+ * @return a new temporary log file.
+ */
+ public static TempLogFile newTempLogFile(final String prefix, final File directory)
+ {
+ IOException fallbackReason = null;
+ if (directory != null)
+ {
+ try
+ {
+ Files.createDirectories(directory.toPath());
+ return new TempLogFile(Files.createTempFile(directory.toPath(), prefix, ".log").toFile());
+ }
+ catch (final IOException e)
+ {
+ // Nothing can be logged yet: the first publisher is the one the constructor installs
+ // below, so the warning has to wait until there is a log to write it to.
+ fallbackReason = e;
+ }
+ }
try
{
- return new TempLogFile(Files.createTempFile(prefix, ".log").toFile());
+ final TempLogFile tempLogFile = new TempLogFile(Files.createTempFile(prefix, ".log").toFile());
+ if (fallbackReason != null)
+ {
+ localizedLogger.warn(LocalizableMessage.raw("Unable to create temp log file in " + directory
+ + " because: " + fallbackReason.getMessage() + ", falling back to the temporary directory"),
+ fallbackReason);
+ }
+ return tempLogFile;
}
catch (final IOException e)
{
@@ -68,9 +111,14 @@
{
this.logFile = null;
this.writer=null;
+ this.startupErrorLogPublisher = null;
+ this.startupDebugLogPublisher = null;
}
final TextWriter writer;
+ /** Kept so that they can be taken off the logger singletons again, see {@link #deleteLogFileAfterSuccess()}. */
+ private final ErrorLogPublisher startupErrorLogPublisher;
+ private final DebugLogPublisher startupDebugLogPublisher;
private TempLogFile(final File file) throws IOException
{
@@ -83,9 +131,9 @@
}else {
writer=new TextWriter.STREAM(new FileOutputStream(file));
}
- ErrorLogPublisher startupErrorLogPublisher = TextErrorLogPublisher.getServerStartupTextErrorPublisher(writer);
+ startupErrorLogPublisher = TextErrorLogPublisher.getServerStartupTextErrorPublisher(writer);
ErrorLogger.getInstance().addLogPublisher(startupErrorLogPublisher);
- DebugLogger.getInstance().addPublisherIfRequired(writer);
+ startupDebugLogPublisher = DebugLogger.getInstance().addPublisherIfRequired(writer);
localizedLogger.info(LocalizableMessage.raw("QuickSetup application launched " + DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(new Date()), null));
}
@@ -100,11 +148,23 @@
return logFile;
}
- /** Closes the log file handler and delete the temp log file . */
+ /**
+ * Closes the log file handler and delete the temp log file .
+ * <p>
+ * The publishers installed by the constructor go with it: they are held by the logger
+ * singletons, which outlive this object, and once the writer is shut everything they are
+ * handed is written to a closed stream and swallowed.
+ */
public void deleteLogFileAfterSuccess()
{
if (isEnabled())
{
+ if (startupErrorLogPublisher != null) {
+ ErrorLogger.getInstance().removeLogPublisher(startupErrorLogPublisher);
+ }
+ if (startupDebugLogPublisher != null) {
+ DebugLogger.getInstance().removeLogPublisher(startupDebugLogPublisher);
+ }
if (writer!=null) {
writer.shutdown();
}
@@ -122,6 +182,40 @@
}
/**
+ * Return {@code true} if the temp log file is still on disk and can be read.
+ * <p>
+ * Unlike {@link #isEnabled()} this is about the file, not the logger: something else may have
+ * removed the file while the logger still writes to it (see issue #1030), and then there is
+ * nothing to hand over to whoever needs the log.
+ *
+ * @return {@code true} if the temp log file is there and readable.
+ */
+ public boolean isReadable()
+ {
+ return logFile != null && Files.isReadable(logFile.toPath()) && Files.isRegularFile(logFile.toPath());
+ }
+
+ /**
+ * Reads the whole temp log file.
+ * <p>
+ * The file is decoded with the default charset of the JVM, which is the one
+ * {@link TextWriter.STREAM} wrote it with: reader and writer are the same JVM, so a
+ * non-ASCII path or base DN in a report comes back as it was logged.
+ *
+ * @return the contents of the temp log file.
+ * @throws IOException
+ * if the file cannot be read, for instance because it is no longer there.
+ */
+ public String readContents() throws IOException
+ {
+ if (logFile == null)
+ {
+ throw new IOException("No temp log file");
+ }
+ return new String(Files.readAllBytes(logFile.toPath()), Charset.defaultCharset());
+ }
+
+ /**
* Return the absolute path of the temp log file.
* @return the absolute path of the temp log file.
*/
--
Gitblit v1.10.0