testFramework: prevent log records multiplication in console

When idea.test.logs.echo.debug.to.stdout is enabled and some
loggers have FINE or lower level allowed then some records might
get multiplicated in the output -- see comment in the code. This
was caused by too many handlers assigned to loggers and this change
reduces the amount of them.

GitOrigin-RevId: 903f52a3f1b01f02303a15b2c633892e13240a89
This commit is contained in:
Evgenii Novozhilov
2025-01-16 17:04:05 +00:00
committed by intellij-monorepo-bot
parent 4675bb4635
commit be1000d644
@@ -9,7 +9,6 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.LineTokenizer;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.ApiStatus.Internal;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -81,22 +80,7 @@ public final class TestLoggerFactory implements Logger.Factory {
myInitialized = true;
}
java.util.logging.Logger julLogger = java.util.logging.Logger.getLogger(category);
if (myEchoDebugToStdout) {
configureLogToStdoutIfDebug(julLogger);
}
return new TestLogger(julLogger, this);
}
/**
* If the logger has the "FINE" level, add a LogToStdoutJulHandler that streams its log records
* to STDOUT with a timestamp relative to the test start time.
*/
private static void configureLogToStdoutIfDebug(@NotNull java.util.logging.Logger julLogger) {
if (julLogger.isLoggable(Level.FINE) &&
ContainerUtil.findInstance(julLogger.getHandlers(), LogToStdoutJulHandler.class) == null) {
julLogger.addHandler(new LogToStdoutJulHandler());
}
return new TestLogger(java.util.logging.Logger.getLogger(category), this);
}
public static int getRethrowErrorNumber() {
@@ -124,6 +108,11 @@ public final class TestLoggerFactory implements Logger.Factory {
Path logFile = logDir.resolve(LOG_FILE_NAME);
JulLogger.clearHandlers();
JulLogger.configureLogFileAndConsole(logFile, false, true, false, null, null, null);
if (myEchoDebugToStdout) {
addConsoleAppenderForDebugRecords();
}
System.out.printf("Test log file: %s%n", logFile);
if (Files.exists(logFile) && Files.size(logFile) >= LOG_SIZE_LIMIT) {
@@ -138,6 +127,13 @@ public final class TestLoggerFactory implements Logger.Factory {
}
}
private static void addConsoleAppenderForDebugRecords() {
java.util.logging.Logger rootLogger = java.util.logging.Logger.getLogger("");
// just add a single console appender instead of multiple handlers, but for the root logger
rootLogger.addHandler(new LogToStdoutJulHandler(Level.FINE));
}
public static @NotNull Path getTestLogDir() {
String property = System.getProperty(PROPERTY_LOG_PATH);
return property == null ? Path.of(PathManager.getSystemPath(), LOG_DIR) : Path.of(property).normalize();
@@ -546,9 +542,13 @@ public final class TestLoggerFactory implements Logger.Factory {
private static class LogToStdoutJulHandler extends StreamHandler {
private boolean initialized;
LogToStdoutJulHandler() {
LogToStdoutJulHandler(Level level) {
super(System.out, new WithTimeSinceTestStartedJulFormatter());
// we'd like to capture all records with level or finer than the level
// so we set level to all and do actual level filtering with the filter
setLevel(Level.ALL);
setFilter(record -> record.getLevel().intValue() <= level.intValue());
}
@Override