diff --git a/src/main/java/net/snowflake/client/core/SFSession.java b/src/main/java/net/snowflake/client/core/SFSession.java index 59aac9d5b..d26da7828 100644 --- a/src/main/java/net/snowflake/client/core/SFSession.java +++ b/src/main/java/net/snowflake/client/core/SFSession.java @@ -46,6 +46,7 @@ import net.snowflake.client.jdbc.telemetry.Telemetry; import net.snowflake.client.jdbc.telemetry.TelemetryClient; import net.snowflake.client.jdbc.telemetryOOB.TelemetryService; +import net.snowflake.client.log.JDK14Logger; import net.snowflake.client.log.SFLogger; import net.snowflake.client.log.SFLoggerFactory; import net.snowflake.client.log.SFLoggerUtil; @@ -435,6 +436,11 @@ public void addSFSessionProperty(String propertyName, Object propertyValue) thro tracingLevel = Level.parse(((String) propertyValue).toUpperCase()); } break; + case JAVA_LOGGING_CONSOLE_STD_OUT: + if (propertyValue != null && (Boolean) propertyValue) { + JDK14Logger.useStdOutConsoleHandler(); + } + break; case DISABLE_SOCKS_PROXY: // note: if any session has this parameter, it will be used for all diff --git a/src/main/java/net/snowflake/client/core/SFSessionProperty.java b/src/main/java/net/snowflake/client/core/SFSessionProperty.java index 97c0adbc2..6a6730c49 100644 --- a/src/main/java/net/snowflake/client/core/SFSessionProperty.java +++ b/src/main/java/net/snowflake/client/core/SFSessionProperty.java @@ -112,7 +112,9 @@ public enum SFSessionProperty { HTTP_CLIENT_CONNECTION_TIMEOUT("HTTP_CLIENT_CONNECTION_TIMEOUT", false, Integer.class), - HTTP_CLIENT_SOCKET_TIMEOUT("HTTP_CLIENT_SOCKET_TIMEOUT", false, Integer.class); + HTTP_CLIENT_SOCKET_TIMEOUT("HTTP_CLIENT_SOCKET_TIMEOUT", false, Integer.class), + + JAVA_LOGGING_CONSOLE_STD_OUT("JAVA_LOGGING_CONSOLE_STD_OUT", false, Boolean.class); // property key in string private String propertyKey; diff --git a/src/main/java/net/snowflake/client/log/JDK14Logger.java b/src/main/java/net/snowflake/client/log/JDK14Logger.java index e9ae25696..466031af2 100644 --- a/src/main/java/net/snowflake/client/log/JDK14Logger.java +++ b/src/main/java/net/snowflake/client/log/JDK14Logger.java @@ -18,6 +18,8 @@ import java.util.logging.SimpleFormatter; import net.snowflake.client.core.EventHandler; import net.snowflake.client.core.EventUtil; +import net.snowflake.client.core.SFSessionProperty; +import net.snowflake.client.core.SnowflakeJdbcInternalApi; import net.snowflake.client.util.SecretDetector; /** @@ -38,10 +40,32 @@ public class JDK14Logger implements SFLogger { public static String STDOUT = "STDOUT"; + private static final StdOutConsoleHandler STD_OUT_CONSOLE_HANDLER = new StdOutConsoleHandler(); + public JDK14Logger(String name) { this.jdkLogger = Logger.getLogger(name); } + static { + String javaLoggingConsoleStdOut = + System.getProperty(SFSessionProperty.JAVA_LOGGING_CONSOLE_STD_OUT.getPropertyKey()); + if ("true".equalsIgnoreCase(javaLoggingConsoleStdOut)) { + useStdOutConsoleHandler(); + } + } + + @SnowflakeJdbcInternalApi + public static void useStdOutConsoleHandler() { + Logger rootLogger = Logger.getLogger(""); + for (Handler handler : rootLogger.getHandlers()) { + if (handler instanceof ConsoleHandler) { + rootLogger.removeHandler(handler); + rootLogger.addHandler(STD_OUT_CONSOLE_HANDLER); + break; + } + } + } + public boolean isDebugEnabled() { return this.jdkLogger.isLoggable(Level.FINE); } diff --git a/src/main/java/net/snowflake/client/log/StdOutConsoleHandler.java b/src/main/java/net/snowflake/client/log/StdOutConsoleHandler.java new file mode 100644 index 000000000..b41659edb --- /dev/null +++ b/src/main/java/net/snowflake/client/log/StdOutConsoleHandler.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2012-2019 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.log; + +import java.util.logging.LogRecord; +import java.util.logging.SimpleFormatter; +import java.util.logging.StreamHandler; + +class StdOutConsoleHandler extends StreamHandler { + public StdOutConsoleHandler() { + // configure with specific defaults for ConsoleHandler + super(System.out, new SimpleFormatter()); + } + + @Override + public void publish(LogRecord record) { + super.publish(record); + flush(); + } + + @Override + public void close() { + flush(); + } +} diff --git a/src/test/java/net/snowflake/client/log/JDK14LoggerConsoleHandlerOverrideLatestIT.java b/src/test/java/net/snowflake/client/log/JDK14LoggerConsoleHandlerOverrideLatestIT.java new file mode 100644 index 000000000..2e91e069d --- /dev/null +++ b/src/test/java/net/snowflake/client/log/JDK14LoggerConsoleHandlerOverrideLatestIT.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.log; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.Arrays; +import java.util.Properties; +import java.util.logging.ConsoleHandler; +import java.util.logging.Handler; +import java.util.logging.Logger; +import net.snowflake.client.category.TestTags; +import net.snowflake.client.jdbc.BaseJDBCTest; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +@Tag(TestTags.CORE) +public class JDK14LoggerConsoleHandlerOverrideLatestIT extends BaseJDBCTest { + /** Added in > 3.20.0 */ + @Test + public void shouldOverrideConsoleLogger() throws Exception { + Properties paramProperties = new Properties(); + paramProperties.put("JAVA_LOGGING_CONSOLE_STD_OUT", true); + try (Connection connection = getConnection(paramProperties); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("select 1")) { + assertTrue(resultSet.next()); + Handler[] handlers = Logger.getLogger("").getHandlers(); + assertTrue(handlers.length > 0); + assertFalse(Arrays.stream(handlers).anyMatch(h -> h instanceof ConsoleHandler)); + assertTrue(Arrays.stream(handlers).anyMatch(h -> h instanceof StdOutConsoleHandler)); + } + } +}