diff --git a/java-bigquery-jdbc/pom.xml b/java-bigquery-jdbc/pom.xml
index 2f344bd29c0a..844a9876605f 100644
--- a/java-bigquery-jdbc/pom.xml
+++ b/java-bigquery-jdbc/pom.xml
@@ -506,7 +506,12 @@
org.apache.maven.plugins
maven-surefire-plugin
- --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED
+
+ --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED --add-opens=java.logging/java.util.logging=ALL-UNNAMED
diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryCallableStatement.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryCallableStatement.java
index e9ad6fa1292d..e1ad8925eb0a 100644
--- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryCallableStatement.java
+++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryCallableStatement.java
@@ -43,7 +43,8 @@
import java.util.Map;
class BigQueryCallableStatement extends BigQueryPreparedStatement implements CallableStatement {
- private final BigQueryJdbcCustomLogger LOG = new BigQueryJdbcCustomLogger(this.toString());
+ private static final BigQueryJdbcCustomLogger LOG =
+ new BigQueryJdbcCustomLogger(BigQueryCallableStatement.class.getName());
private boolean lastReadWasNull = false;
private Object getParameterAndTrackNull(int parameterIndex) throws SQLException {
diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnection.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnection.java
index 1e1c84c8e88c..6189f7ea75be 100644
--- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnection.java
+++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnection.java
@@ -92,7 +92,8 @@
*/
public class BigQueryConnection extends BigQueryNoOpsConnection {
- private final BigQueryJdbcCustomLogger LOG = new BigQueryJdbcCustomLogger(this.toString());
+ private static final BigQueryJdbcCustomLogger LOG =
+ new BigQueryJdbcCustomLogger(BigQueryConnection.class.getName());
String connectionClassName = this.toString();
private final String connectionId;
private static final String DEFAULT_JDBC_TOKEN_VALUE = "Google-BigQuery-JDBC-Driver";
diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java
index a24522482d6e..01ca547234ae 100644
--- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java
+++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaData.java
@@ -91,7 +91,8 @@
* @see BigQueryStatement
*/
class BigQueryDatabaseMetaData implements DatabaseMetaData {
- final BigQueryJdbcCustomLogger LOG = new BigQueryJdbcCustomLogger(this.toString());
+ static final BigQueryJdbcCustomLogger LOG =
+ new BigQueryJdbcCustomLogger(BigQueryDatabaseMetaData.class.getName());
private static final String DATABASE_PRODUCT_NAME = "Google BigQuery";
private static final String DATABASE_PRODUCT_VERSION = "2.0";
private static final String DRIVER_NAME = "GoogleJDBCDriverForGoogleBigQuery";
diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcCustomLogger.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcCustomLogger.java
index c4bc39f7ee2a..0acea77e5bf6 100644
--- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcCustomLogger.java
+++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcCustomLogger.java
@@ -30,7 +30,6 @@ protected BigQueryJdbcCustomLogger(String name, String resourceBundleName) {
public BigQueryJdbcCustomLogger(String name) {
this(name, null);
- this.setParent(BigQueryJdbcRootLogger.getRootLogger());
}
private void logWithCaller(Level level, Supplier msgSupplier) {
diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryPooledConnection.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryPooledConnection.java
index ad62ee76bcdb..6b90421d5d3d 100644
--- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryPooledConnection.java
+++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryPooledConnection.java
@@ -31,7 +31,8 @@
import javax.sql.StatementEventListener;
class BigQueryPooledConnection implements PooledConnection {
- private final BigQueryJdbcCustomLogger LOG = new BigQueryJdbcCustomLogger(this.toString());
+ private static final BigQueryJdbcCustomLogger LOG =
+ new BigQueryJdbcCustomLogger(BigQueryPooledConnection.class.getName());
private String id; // Mainly for internal use
private Connection bqConnection;
@@ -165,7 +166,8 @@ public void removeStatementEventListener(StatementEventListener arg0) {
// This class notifies the listeners or calls the listner notification methods
// provided by the pooled connection.
static class BigQueryPooledConnectionWrapper implements Connection {
- private final BigQueryJdbcCustomLogger LOG = new BigQueryJdbcCustomLogger(this.toString());
+ private static final BigQueryJdbcCustomLogger LOG =
+ new BigQueryJdbcCustomLogger(BigQueryPooledConnectionWrapper.class.getName());
private Connection bqConnectionDelegate;
private BigQueryPooledConnection pooledConnection;
private boolean closed = false;
diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryStatement.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryStatement.java
index 0654d6036f0f..df0d135006ef 100644
--- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryStatement.java
+++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryStatement.java
@@ -102,7 +102,8 @@
*/
public class BigQueryStatement extends BigQueryNoOpsStatement {
- private final BigQueryJdbcCustomLogger LOG = new BigQueryJdbcCustomLogger(this.toString());
+ private static final BigQueryJdbcCustomLogger LOG =
+ new BigQueryJdbcCustomLogger(BigQueryStatement.class.getName());
public static final int DEFAULT_BUFFER_SIZE = BigQuerySettings.DEFAULT_NUM_BUFFERED_ROWS * 2;
static final int DEFAULT_ARROW_BUFFER_SIZE = 32;
private static final String DEFAULT_DATASET_NAME = "_google_jdbc";
diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/DataSource.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/DataSource.java
index d0485584e15d..66034dc1a35d 100644
--- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/DataSource.java
+++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/DataSource.java
@@ -43,7 +43,8 @@
* registered with a naming service based on the Java™ Naming and Directory (JNDI) API.
*/
public class DataSource implements javax.sql.DataSource {
- private final BigQueryJdbcCustomLogger LOG = new BigQueryJdbcCustomLogger(this.toString());
+ private static final BigQueryJdbcCustomLogger LOG =
+ new BigQueryJdbcCustomLogger(DataSource.class.getName());
private String URL;
static final ImmutableSet VALID_JOB_CREATION_MODES = ImmutableSet.of(1, 2);
diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/PooledConnectionListener.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/PooledConnectionListener.java
index 1534a6a0b798..86bc9626a08b 100644
--- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/PooledConnectionListener.java
+++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/PooledConnectionListener.java
@@ -23,7 +23,8 @@
import javax.sql.PooledConnection;
public class PooledConnectionListener implements ConnectionEventListener {
- private final BigQueryJdbcCustomLogger LOG = new BigQueryJdbcCustomLogger(this.toString());
+ private static final BigQueryJdbcCustomLogger LOG =
+ new BigQueryJdbcCustomLogger(PooledConnectionListener.class.getName());
private String id; // Mainly for internal use
private LinkedBlockingDeque connectionPool;
private Long connectionPoolSize = 0L;
diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcCustomLoggerTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcCustomLoggerTest.java
index 38a5df969406..ab6fa432b770 100644
--- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcCustomLoggerTest.java
+++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcCustomLoggerTest.java
@@ -20,6 +20,8 @@
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import com.google.cloud.bigquery.Field;
import com.google.cloud.bigquery.FieldList;
@@ -37,6 +39,7 @@
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
+import java.util.logging.Logger;
import org.apache.arrow.vector.util.JsonStringArrayList;
import org.apache.arrow.vector.util.JsonStringHashMap;
import org.junit.jupiter.api.AfterEach;
@@ -49,6 +52,8 @@ public class BigQueryJdbcCustomLoggerTest extends BigQueryJdbcLoggingBaseTest {
private BigQueryJdbcCustomLogger logger;
private TestHandler testHandler;
+ private static final int OBJECT_COUNT = 1_000;
+
private static class TestHandler extends Handler {
private final List records = new ArrayList<>();
@@ -579,4 +584,69 @@ public void testNestedArrayInJsonStructConnectionIdPropagation() throws Exceptio
"Log from Nested JSON Array",
() -> nestedArray.LOG.fine("Log from Nested JSON Array"));
}
+
+ @Test
+ public void testLoggerIsParentedToDriverRootLogger() {
+ assertSame(
+ BigQueryJdbcRootLogger.getRootLogger(),
+ new BigQueryJdbcCustomLogger("ParentCheck").getParent());
+ }
+
+ @Test
+ public void testCreatingStatementsDoesNotRegisterChildLoggers() throws Exception {
+ BigQueryConnection connection = mock(BigQueryConnection.class);
+ doReturn("leak-test-connection").when(connection).getConnectionId();
+ doReturn(BigQueryConnection.SessionState.empty()).when(connection).getSessionStateSnapshot();
+
+ Logger rootLogger = BigQueryJdbcRootLogger.getRootLogger();
+ // Warm up so that any one-time, class-level logger is registered before we measure.
+ new BigQueryStatement(connection);
+ int childrenBefore = childLoggerCount(rootLogger);
+
+ for (int i = 0; i < OBJECT_COUNT; i++) {
+ new BigQueryStatement(connection);
+ }
+
+ assertEquals(
+ childrenBefore,
+ childLoggerCount(rootLogger),
+ "Creating "
+ + OBJECT_COUNT
+ + " statements must not register additional child loggers on the driver root logger");
+ }
+
+ @Test
+ public void testCreatingDatabaseMetaDataDoesNotRegisterChildLoggers()
+ throws ReflectiveOperationException {
+ BigQueryConnection connection = mock(BigQueryConnection.class);
+ doReturn("leak-test-connection").when(connection).getConnectionId();
+
+ Logger rootLogger = BigQueryJdbcRootLogger.getRootLogger();
+ new BigQueryDatabaseMetaData(connection);
+ int childrenBefore = childLoggerCount(rootLogger);
+
+ for (int i = 0; i < OBJECT_COUNT; i++) {
+ new BigQueryDatabaseMetaData(connection);
+ }
+
+ assertEquals(
+ childrenBefore,
+ childLoggerCount(rootLogger),
+ "Creating "
+ + OBJECT_COUNT
+ + " DatabaseMetaData objects must not register additional child loggers on the driver"
+ + " root logger");
+ }
+
+ /**
+ * Reads the JDK-internal {@code Logger.kids} list. This field is private to {@code java.logging},
+ * so the surefire {@code argLine} opens {@code java.util.logging} to the test classpath.
+ */
+ private static int childLoggerCount(Logger logger) throws ReflectiveOperationException {
+ // Qualified because this file already imports com.google.cloud.bigquery.Field.
+ java.lang.reflect.Field kids = Logger.class.getDeclaredField("kids");
+ kids.setAccessible(true);
+ List> children = (List>) kids.get(logger);
+ return children == null ? 0 : children.size();
+ }
}