Skip to content

Commit c9bf0e7

Browse files
SNOW-1652680: Add threshold for j.u.l. stderr stdout logging (#1988)
1 parent 6da3ca3 commit c9bf0e7

File tree

7 files changed

+253
-17
lines changed

7 files changed

+253
-17
lines changed

src/main/java/net/snowflake/client/core/SFSession.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ public class SFSession extends SFBaseSession {
153153
*/
154154
private Duration browserResponseTimeout = Duration.ofSeconds(120);
155155

156+
private boolean javaUtilLoggingConsoleOut = false;
157+
private String javaUtilLoggingConsoleOutThreshold = null;
158+
156159
// This constructor is used only by tests with no real connection.
157160
// For real connections, the other constructor is always used.
158161
@VisibleForTesting
@@ -437,8 +440,13 @@ public void addSFSessionProperty(String propertyName, Object propertyValue) thro
437440
}
438441
break;
439442
case JAVA_LOGGING_CONSOLE_STD_OUT:
440-
if (propertyValue != null && (Boolean) propertyValue) {
441-
JDK14Logger.useStdOutConsoleHandler();
443+
if (propertyValue != null) {
444+
javaUtilLoggingConsoleOut = (Boolean) propertyValue;
445+
}
446+
break;
447+
case JAVA_LOGGING_CONSOLE_STD_OUT_THRESHOLD:
448+
if (propertyValue != null) {
449+
javaUtilLoggingConsoleOutThreshold = (String) propertyValue;
442450
}
443451
break;
444452

@@ -562,6 +570,13 @@ public void addSFSessionProperty(String propertyName, Object propertyValue) thro
562570
}
563571
}
564572

573+
@SnowflakeJdbcInternalApi
574+
public void overrideConsoleHandlerWhenNecessary() {
575+
if (javaUtilLoggingConsoleOut) {
576+
JDK14Logger.useStdOutConsoleHandler(javaUtilLoggingConsoleOutThreshold);
577+
}
578+
}
579+
565580
public boolean containProperty(String key) {
566581
return sessionParametersMap.containsKey(key);
567582
}

src/main/java/net/snowflake/client/core/SFSessionProperty.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,9 @@ public enum SFSessionProperty {
114114

115115
HTTP_CLIENT_SOCKET_TIMEOUT("HTTP_CLIENT_SOCKET_TIMEOUT", false, Integer.class),
116116

117-
JAVA_LOGGING_CONSOLE_STD_OUT("JAVA_LOGGING_CONSOLE_STD_OUT", false, Boolean.class);
117+
JAVA_LOGGING_CONSOLE_STD_OUT("JAVA_LOGGING_CONSOLE_STD_OUT", false, Boolean.class),
118+
JAVA_LOGGING_CONSOLE_STD_OUT_THRESHOLD(
119+
"JAVA_LOGGING_CONSOLE_STD_OUT_THRESHOLD", false, String.class);
118120

119121
// property key in string
120122
private String propertyKey;

src/main/java/net/snowflake/client/jdbc/DefaultSFConnectionHandler.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ private void initSessionProperties(SnowflakeConnectString conStr, String appID,
337337
}
338338
sfSession.addSFSessionProperty(property.getKey(), property.getValue());
339339
}
340+
sfSession.overrideConsoleHandlerWhenNecessary();
340341

341342
// populate app id and version
342343
sfSession.addProperty(SFSessionProperty.APP_ID, appID);

src/main/java/net/snowflake/client/log/JDK14Logger.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,46 @@ public JDK14Logger(String name) {
5050
String javaLoggingConsoleStdOut =
5151
System.getProperty(SFSessionProperty.JAVA_LOGGING_CONSOLE_STD_OUT.getPropertyKey());
5252
if ("true".equalsIgnoreCase(javaLoggingConsoleStdOut)) {
53-
useStdOutConsoleHandler();
53+
String javaLoggingConsoleStdOutThreshold =
54+
System.getProperty(
55+
SFSessionProperty.JAVA_LOGGING_CONSOLE_STD_OUT_THRESHOLD.getPropertyKey());
56+
useStdOutConsoleHandler(javaLoggingConsoleStdOutThreshold);
5457
}
5558
}
5659

5760
@SnowflakeJdbcInternalApi
58-
public static void useStdOutConsoleHandler() {
61+
public static void useStdOutConsoleHandler(String threshold) {
62+
Level thresholdLevel = threshold != null ? tryParse(threshold) : null;
5963
Logger rootLogger = Logger.getLogger("");
6064
for (Handler handler : rootLogger.getHandlers()) {
6165
if (handler instanceof ConsoleHandler) {
6266
rootLogger.removeHandler(handler);
63-
rootLogger.addHandler(STD_OUT_CONSOLE_HANDLER);
67+
if (thresholdLevel != null) {
68+
rootLogger.addHandler(new StdErrOutThresholdAwareConsoleHandler(thresholdLevel));
69+
} else {
70+
rootLogger.addHandler(new StdOutConsoleHandler());
71+
}
72+
break;
73+
}
74+
}
75+
}
76+
77+
private static Level tryParse(String threshold) {
78+
try {
79+
return Level.parse(threshold);
80+
} catch (Exception e) {
81+
throw new UnknownJavaUtilLoggingLevelException(threshold);
82+
}
83+
}
84+
85+
@SnowflakeJdbcInternalApi
86+
static void resetToDefaultConsoleHandler() {
87+
Logger rootLogger = Logger.getLogger("");
88+
for (Handler handler : rootLogger.getHandlers()) {
89+
if (handler instanceof StdErrOutThresholdAwareConsoleHandler
90+
|| handler instanceof StdOutConsoleHandler) {
91+
rootLogger.removeHandler(handler);
92+
rootLogger.addHandler(new ConsoleHandler());
6493
break;
6594
}
6695
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (c) 2012-2019 Snowflake Computing Inc. All rights reserved.
3+
*/
4+
package net.snowflake.client.log;
5+
6+
import java.util.logging.ConsoleHandler;
7+
import java.util.logging.Level;
8+
import java.util.logging.LogRecord;
9+
import java.util.logging.SimpleFormatter;
10+
import java.util.logging.StreamHandler;
11+
12+
class StdErrOutThresholdAwareConsoleHandler extends StreamHandler {
13+
private final ConsoleHandler stdErrConsoleHandler = new ConsoleHandler();
14+
private final Level threshold;
15+
16+
public StdErrOutThresholdAwareConsoleHandler(Level threshold) {
17+
super(System.out, new SimpleFormatter());
18+
this.threshold = threshold;
19+
}
20+
21+
@Override
22+
public void publish(LogRecord record) {
23+
if (record.getLevel().intValue() > threshold.intValue()) {
24+
stdErrConsoleHandler.publish(record);
25+
} else {
26+
super.publish(record);
27+
flush();
28+
}
29+
}
30+
31+
@Override
32+
public void close() {
33+
flush();
34+
stdErrConsoleHandler.close();
35+
}
36+
37+
Level getThreshold() {
38+
return threshold;
39+
}
40+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (c) 2012-2019 Snowflake Computing Inc. All rights reserved.
3+
*/
4+
package net.snowflake.client.log;
5+
6+
import java.util.logging.Level;
7+
import java.util.stream.Collectors;
8+
import java.util.stream.Stream;
9+
10+
class UnknownJavaUtilLoggingLevelException extends RuntimeException {
11+
private static final String AVAILABLE_LEVELS =
12+
Stream.of(
13+
Level.OFF,
14+
Level.SEVERE,
15+
Level.WARNING,
16+
Level.INFO,
17+
Level.CONFIG,
18+
Level.FINE,
19+
Level.FINER,
20+
Level.FINEST,
21+
Level.ALL)
22+
.map(Level::getName)
23+
.collect(Collectors.joining(", "));
24+
25+
UnknownJavaUtilLoggingLevelException(String threshold) {
26+
super(
27+
"Unknown java util logging level: " + threshold + ", expected one of: " + AVAILABLE_LEVELS);
28+
}
29+
}

src/test/java/net/snowflake/client/log/JDK14LoggerConsoleHandlerOverrideLatestIT.java

Lines changed: 131 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,157 @@
33
*/
44
package net.snowflake.client.log;
55

6+
import static org.junit.jupiter.api.Assertions.assertEquals;
67
import static org.junit.jupiter.api.Assertions.assertFalse;
8+
import static org.junit.jupiter.api.Assertions.assertThrows;
79
import static org.junit.jupiter.api.Assertions.assertTrue;
810

11+
import java.io.ByteArrayOutputStream;
12+
import java.io.PrintStream;
913
import java.sql.Connection;
10-
import java.sql.ResultSet;
11-
import java.sql.Statement;
14+
import java.sql.SQLException;
1215
import java.util.Arrays;
1316
import java.util.Properties;
1417
import java.util.logging.ConsoleHandler;
1518
import java.util.logging.Handler;
19+
import java.util.logging.Level;
1620
import java.util.logging.Logger;
1721
import net.snowflake.client.category.TestTags;
1822
import net.snowflake.client.jdbc.BaseJDBCTest;
23+
import org.junit.jupiter.api.AfterAll;
24+
import org.junit.jupiter.api.BeforeAll;
25+
import org.junit.jupiter.api.BeforeEach;
1926
import org.junit.jupiter.api.Tag;
2027
import org.junit.jupiter.api.Test;
2128

2229
@Tag(TestTags.CORE)
2330
public class JDK14LoggerConsoleHandlerOverrideLatestIT extends BaseJDBCTest {
31+
private static final PrintStream standardOut = System.out;
32+
private static final PrintStream standardErr = System.err;
33+
private static final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
34+
private static final ByteArrayOutputStream errStream = new ByteArrayOutputStream();
35+
private static final String errorMessage = "error message 1";
36+
private static final String warningMessage = "warning message 1";
37+
private static final String infoMessage = "info message 1";
38+
private static final String debugMessage = "debug message 1";
39+
40+
@BeforeAll
41+
public static void replaceStreams() {
42+
System.setOut(new PrintStream(outputStream));
43+
System.setErr(new PrintStream(errStream));
44+
}
45+
46+
@AfterAll
47+
public static void resetStreams() {
48+
System.setOut(standardOut);
49+
System.setErr(standardErr);
50+
}
51+
52+
/** Added in > 3.20.0 */
53+
@Test
54+
public void shouldLogAllToStdErr() throws Exception {
55+
Properties paramProperties = new Properties();
56+
57+
connectAndLog(paramProperties);
58+
59+
Handler[] handlers = Logger.getLogger("").getHandlers();
60+
assertTrue(handlers.length > 0);
61+
assertTrue(Arrays.stream(handlers).anyMatch(h -> h instanceof ConsoleHandler));
62+
63+
System.out.flush();
64+
System.err.flush();
65+
assertEquals("", outputStream.toString());
66+
// overriding stderr does not work correctly with maven
67+
// String errString = errStream.toString();
68+
// assertTrue(errString.contains(errorMessage), () -> "STDERR: " + errString);
69+
// assertTrue(errString.contains(warningMessage), () -> "STDERR: " + errString);
70+
// assertTrue(errString.contains(infoMessage), () -> "STDERR: " + errString);
71+
// assertFalse(errString.contains(debugMessage), () -> "STDERR: " + errString);
72+
}
73+
2474
/** Added in > 3.20.0 */
2575
@Test
26-
public void shouldOverrideConsoleLogger() throws Exception {
76+
public void shouldOverrideConsoleLoggerToStdOut() throws Exception {
2777
Properties paramProperties = new Properties();
2878
paramProperties.put("JAVA_LOGGING_CONSOLE_STD_OUT", true);
29-
try (Connection connection = getConnection(paramProperties);
30-
Statement statement = connection.createStatement();
31-
ResultSet resultSet = statement.executeQuery("select 1")) {
32-
assertTrue(resultSet.next());
33-
Handler[] handlers = Logger.getLogger("").getHandlers();
34-
assertTrue(handlers.length > 0);
35-
assertFalse(Arrays.stream(handlers).anyMatch(h -> h instanceof ConsoleHandler));
36-
assertTrue(Arrays.stream(handlers).anyMatch(h -> h instanceof StdOutConsoleHandler));
79+
80+
connectAndLog(paramProperties);
81+
82+
Handler[] handlers = Logger.getLogger("").getHandlers();
83+
assertTrue(handlers.length > 0);
84+
assertFalse(Arrays.stream(handlers).anyMatch(h -> h instanceof ConsoleHandler));
85+
assertTrue(Arrays.stream(handlers).anyMatch(h -> h instanceof StdOutConsoleHandler));
86+
87+
System.out.flush();
88+
System.err.flush();
89+
String outString = outputStream.toString();
90+
assertTrue(outString.contains(errorMessage), () -> "STDOUT: " + outString);
91+
assertTrue(outString.contains(warningMessage), () -> "STDOUT: " + outString);
92+
assertTrue(outString.contains(infoMessage), () -> "STDOUT: " + outString);
93+
assertFalse(outString.contains(debugMessage), () -> "STDOUT: " + outString);
94+
// overriding stderr does not work correctly with maven
95+
// assertEquals("", errStream.toString());
96+
}
97+
98+
/** Added in > 3.20.0 */
99+
@Test
100+
public void shouldOverrideConsoleLoggerWithSpecificThreshold() throws Exception {
101+
Properties paramProperties = new Properties();
102+
paramProperties.put("JAVA_LOGGING_CONSOLE_STD_OUT", true);
103+
paramProperties.put("JAVA_LOGGING_CONSOLE_STD_OUT_THRESHOLD", "WARNING");
104+
105+
connectAndLog(paramProperties);
106+
107+
Handler[] handlers = Logger.getLogger("").getHandlers();
108+
assertTrue(handlers.length > 0);
109+
assertFalse(Arrays.stream(handlers).anyMatch(h -> h instanceof ConsoleHandler));
110+
assertTrue(
111+
Arrays.stream(handlers)
112+
.anyMatch(
113+
h ->
114+
h instanceof StdErrOutThresholdAwareConsoleHandler
115+
&& ((StdErrOutThresholdAwareConsoleHandler) h)
116+
.getThreshold()
117+
.equals(Level.WARNING)));
118+
119+
System.out.flush();
120+
System.err.flush();
121+
String outString = outputStream.toString();
122+
assertFalse(outString.contains(errorMessage), () -> "STDOUT: " + outString);
123+
assertTrue(outString.contains(warningMessage), () -> "STDOUT: " + outString);
124+
assertTrue(outString.contains(infoMessage), () -> "STDOUT: " + outString);
125+
assertFalse(outString.contains(debugMessage), () -> "STDOUT: " + outString);
126+
// overriding stderr does not work correctly with maven
127+
// String errString = errStream.toString();
128+
// assertTrue(errString.contains(errorMessage), () -> "STDERR: " + errString);
129+
// assertFalse(errString.contains(warningMessage), () -> "STDERR: " + errString);
130+
// assertFalse(errString.contains(infoMessage), () -> "STDERR: " + errString);
131+
// assertFalse(errString.contains(debugMessage), () -> "STDERR: " + errString);
132+
}
133+
134+
private static void connectAndLog(Properties paramProperties) throws SQLException {
135+
try (Connection con = getConnection(paramProperties)) {
136+
SFLogger logger = SFLoggerFactory.getLogger(JDK14LoggerConsoleHandlerOverrideLatestIT.class);
137+
logger.error(errorMessage);
138+
logger.warn(warningMessage);
139+
logger.info(infoMessage);
140+
logger.debug(debugMessage);
37141
}
38142
}
143+
144+
/** Added in > 3.20.0 */
145+
@Test
146+
public void shouldThrowExceptionOnUnknownLevel() throws Exception {
147+
Properties paramProperties = new Properties();
148+
paramProperties.put("JAVA_LOGGING_CONSOLE_STD_OUT", true);
149+
paramProperties.put("JAVA_LOGGING_CONSOLE_STD_OUT_THRESHOLD", "UNKNOWN");
150+
assertThrows(UnknownJavaUtilLoggingLevelException.class, () -> getConnection(paramProperties));
151+
}
152+
153+
@BeforeEach
154+
public void reset() {
155+
JDK14Logger.resetToDefaultConsoleHandler();
156+
outputStream.reset();
157+
errStream.reset();
158+
}
39159
}

0 commit comments

Comments
 (0)