diff --git a/java-bigquery/google-cloud-bigquery-jdbc/.gitignore b/java-bigquery/google-cloud-bigquery-jdbc/.gitignore index 2c4fba513e11..4e422d646dee 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/.gitignore +++ b/java-bigquery/google-cloud-bigquery-jdbc/.gitignore @@ -1,7 +1,8 @@ drivers/** target-it/** -*logs*/** +**/*logs*/** **/ITBigQueryJDBCLocalTest.java +**/BigQueryStatementE2EBenchmark.java tools/**/*.class tools/**/*.jfr \ No newline at end of file diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowArray.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowArray.java index 49bd565df781..a697023a19bc 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowArray.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowArray.java @@ -28,8 +28,8 @@ * An implementation of {@link BigQueryBaseArray} used to represent Array values from Arrow data. */ class BigQueryArrowArray extends BigQueryBaseArray { - private static final BigQueryJdbcCustomLogger LOG = - new BigQueryJdbcCustomLogger(BigQueryArrowArray.class.getName()); + private static final BigQueryJdbcResultSetLogger LOG = + BigQueryJdbcResultSetLogger.getLogger(BigQueryArrowArray.class); private static final BigQueryTypeCoercer BIGQUERY_TYPE_COERCER = BigQueryTypeCoercionUtility.INSTANCE; private JsonStringArrayList values; @@ -41,7 +41,7 @@ public BigQueryArrowArray(Field schema, JsonStringArrayList values) { @Override public Object getArray() { - LOG.finest("++enter++"); + LOG.finestTrace("getArray", "++enter++"); ensureValid(); if (values == null) { return null; @@ -51,7 +51,7 @@ public Object getArray() { @Override public Object getArray(long index, int count) { - LOG.finest("++enter++"); + LOG.finestTrace("getArray", "++enter++"); ensureValid(); if (values == null) { return null; @@ -62,7 +62,7 @@ public Object getArray(long index, int count) { @Override public ResultSet getResultSet() throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getResultSet", "++enter++"); ensureValid(); if (values == null) { return new BigQueryArrowResultSet(); @@ -75,7 +75,7 @@ public ResultSet getResultSet() throws SQLException { @Override public ResultSet getResultSet(long index, int count) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getResultSet", "++enter++"); ensureValid(); if (values == null) { return new BigQueryArrowResultSet(); @@ -89,14 +89,14 @@ public ResultSet getResultSet(long index, int count) throws SQLException { @Override public void free() { - LOG.finest("++enter++"); + LOG.finestTrace("free", "++enter++"); this.values = null; markInvalid(); } @Override Object getCoercedValue(int index) { - LOG.finest("++enter++"); + LOG.finestTrace("getCoercedValue", "++enter++"); Object value = this.values.get(index); return this.arrayOfStruct ? new BigQueryArrowStruct(schema.getSubFields(), (JsonStringHashMap) value) diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java index 0736cd309f57..9cdf3dfd98c2 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java @@ -96,7 +96,7 @@ private BigQueryArrowResultSet( BigQuery bigQuery) throws SQLException { super(bigQuery, statement, schema, isNested); - LOG.finest("++enter++"); + LOG.finestTrace("", "++enter++"); this.totalRows = totalRows; this.buffer = buffer; this.currentNestedBatch = currentNestedBatch; @@ -181,7 +181,7 @@ private ArrowDeserializer(ArrowSchema arrowSchema) throws IOException { } private void deserializeArrowBatch(ArrowRecordBatch batch) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("deserializeArrowBatch", "++enter++"); try { if (vectorSchemaRoot != null) { // Clear vectorSchemaRoot before populating a new batch @@ -204,7 +204,7 @@ private void deserializeArrowBatch(ArrowRecordBatch batch) throws SQLException { @Override public void close() { - LOG.finest("++enter++"); + LOG.fineTrace("close", () -> String.format("Closing BigQueryArrowResultSet %s.", this)); vectorSchemaRoot.close(); allocator.close(); } @@ -276,7 +276,7 @@ else if (this.currentBatchRowIndex < this.vectorSchemaRoot.getRowCount()) { } private Object getObjectInternal(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getObjectInternal", "++enter++"); checkClosed(); Object value; if (this.isNested) { @@ -318,7 +318,7 @@ private Object getObjectInternal(int columnIndex) throws SQLException { public Object getObject(int columnIndex) throws SQLException { // columnIndex is SQL index starting at 1 - LOG.finest("++enter++"); + LOG.finestTrace("getObject", "++enter++"); checkClosed(); Object value = getObjectInternal(columnIndex); if (value == null) { @@ -449,7 +449,7 @@ private String formatRangeElement(Object element, StandardSQLTypeName elementTyp @Override public void close() { - LOG.fine("Closing BigqueryArrowResultSet %s.", this); + LOG.fineTrace("close", () -> String.format("Closing BigqueryArrowResultSet %s.", this)); this.isClosed = true; if (ownedThread != null && !ownedThread.isInterrupted()) { // interrupt the producer thread when result set is closed @@ -460,7 +460,7 @@ public void close() { @Override public boolean isBeforeFirst() throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("isBeforeFirst", "++enter++"); checkClosed(); if (this.isNested) { return this.nestedRowIndex < this.fromIndex; @@ -471,14 +471,14 @@ public boolean isBeforeFirst() throws SQLException { @Override public boolean isAfterLast() throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("isAfterLast", "++enter++"); checkClosed(); return this.afterLast; } @Override public boolean isFirst() throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("isFirst", "++enter++"); checkClosed(); if (this.isNested) { return this.nestedRowIndex == this.fromIndex; @@ -489,7 +489,7 @@ public boolean isFirst() throws SQLException { @Override public boolean isLast() throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("isLast", "++enter++"); checkClosed(); if (this.isNested) { return this.nestedRowIndex == this.toIndexExclusive - 1; diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java index 33befe902be4..54ca4cb96547 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java @@ -30,8 +30,8 @@ * An implementation of {@link BigQueryBaseStruct} used to represent Struct values from Arrow data. */ class BigQueryArrowStruct extends BigQueryBaseStruct { - private static final BigQueryJdbcCustomLogger LOG = - new BigQueryJdbcCustomLogger(BigQueryArrowStruct.class.getName()); + private static final BigQueryJdbcResultSetLogger LOG = + BigQueryJdbcResultSetLogger.getLogger(BigQueryArrowStruct.class); private static final BigQueryTypeCoercer BIGQUERY_TYPE_COERCER = BigQueryTypeCoercionUtility.INSTANCE; @@ -52,7 +52,7 @@ FieldList getSchema() { @Override public Object[] getAttributes() { - LOG.finest("++enter++"); + LOG.finestTrace("getAttributes", "++enter++"); int size = this.schema.size(); Object[] attributes = (Object[]) Array.newInstance(Object.class, size); @@ -71,7 +71,7 @@ public Object[] getAttributes() { } private Object getValue(Field currentSchema, Object currentValue) { - LOG.finest("++enter++"); + LOG.finestTrace("getValue", "++enter++"); if (isArray(currentSchema)) { return new BigQueryArrowArray(currentSchema, (JsonStringArrayList) currentValue); } else if (isStruct(currentSchema)) { diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseArray.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseArray.java index 4cff4db9d379..a48a3b25d141 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseArray.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseArray.java @@ -41,8 +41,8 @@ * reference to an SQL ARRAY value. */ abstract class BigQueryBaseArray implements java.sql.Array { - private static final BigQueryJdbcCustomLogger LOG = - new BigQueryJdbcCustomLogger(BigQueryBaseArray.class.getName()); + private static final BigQueryJdbcResultSetLogger LOG = + BigQueryJdbcResultSetLogger.getLogger(BigQueryBaseArray.class); protected final boolean arrayOfStruct; private boolean valid; @@ -56,14 +56,14 @@ abstract class BigQueryBaseArray implements java.sql.Array { @Override public final String getBaseTypeName() { - LOG.finest("++enter++"); + LOG.finestTrace("getBaseTypeName", "++enter++"); ensureValid(); return this.schema.getType().getStandardType().name(); } @Override public final int getBaseType() { - LOG.finest("++enter++"); + LOG.finestTrace("getBaseType", "++enter++"); ensureValid(); return BigQueryJdbcTypeMappings.standardSQLToJavaSqlTypesMapping.get( schema.getType().getStandardType()); @@ -92,7 +92,7 @@ public final ResultSet getResultSet(long index, int count, Map> } protected Object getArrayInternal(int fromIndex, int toIndexExclusive) { - LOG.finest("++enter++"); + LOG.finestTrace("getArrayInternal", "++enter++"); Class targetClass = getTargetClass(); int size = toIndexExclusive - fromIndex; Object javaArray = Array.newInstance(targetClass, size); @@ -104,7 +104,7 @@ protected Object getArrayInternal(int fromIndex, int toIndexExclusive) { } protected void ensureValid() throws IllegalStateException { - LOG.finest("++enter++"); + LOG.finestTrace("ensureValid", "++enter++"); if (!this.valid) { IllegalStateException ex = new IllegalStateException(INVALID_ARRAY); LOG.severe(INVALID_ARRAY, ex); @@ -113,19 +113,19 @@ protected void ensureValid() throws IllegalStateException { } protected void markInvalid() { - LOG.finest("++enter++"); + LOG.finestTrace("markInvalid", "++enter++"); this.schema = null; this.valid = false; } protected Field singleElementSchema() { - LOG.finest("++enter++"); + LOG.finestTrace("singleElementSchema", "++enter++"); return this.schema.toBuilder().setMode(Mode.REQUIRED).build(); } protected Tuple createRange(long index, int count, int size) throws IllegalStateException { - LOG.finest("++enter++"); + LOG.finestTrace("createRange", "++enter++"); // jdbc array follows 1 based array indexing long normalisedFromIndex = index - 1; if (normalisedFromIndex + count > size) { @@ -142,7 +142,7 @@ protected Tuple createRange(long index, int count, int size) } protected Class getTargetClass() { - LOG.finest("++enter++"); + LOG.finestTrace("getTargetClass", "++enter++"); return this.arrayOfStruct ? Struct.class : BigQueryJdbcTypeMappings.standardSQLToJavaTypeMapping.get( @@ -152,7 +152,7 @@ protected Class getTargetClass() { abstract Object getCoercedValue(int index); static boolean isArray(Field currentSchema) { - LOG.finest("++enter++"); + LOG.finestTrace("isArray", "++enter++"); return currentSchema.getMode() == REPEATED; } diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java index 0e72a5fccf71..0199ada7fb70 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java @@ -46,8 +46,7 @@ public abstract class BigQueryBaseResultSet extends BigQueryNoOpsResultSet implements BigQueryResultSet { - protected final BigQueryJdbcCustomLogger LOG = - new BigQueryJdbcCustomLogger(this.getClass().getName()); + protected final BigQueryJdbcResultSetLogger LOG; private BigQuery bigQuery; private JobId jobId; private String queryId; @@ -67,6 +66,9 @@ protected BigQueryBaseResultSet( this.schema = schema; this.schemaFieldList = schema != null ? schema.getFields() : null; this.isNested = isNested; + this.LOG = + BigQueryJdbcResultSetLogger.getLogger( + this.getClass(), statement != null ? statement.connectionId : null); } public QueryStatistics getQueryStatistics() { @@ -245,7 +247,7 @@ public boolean isClosed() { public abstract Object getObject(int columnIndex) throws SQLException; protected int getColumnIndex(String columnLabel) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getColumnIndex", "++enter++"); checkClosed(); if (columnLabel == null) { throw logAndCreateException("Column label cannot be null"); @@ -256,7 +258,7 @@ protected int getColumnIndex(String columnLabel) throws SQLException { @Override public String getString(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getString", "++enter++"); try { Object value = getObject(columnIndex); return this.bigQueryTypeCoercer.coerceTo(String.class, value); @@ -267,7 +269,7 @@ public String getString(int columnIndex) throws SQLException { @Override public boolean getBoolean(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getBoolean", "++enter++"); StandardSQLTypeName type = getStandardSQLTypeName(columnIndex); if (type == StandardSQLTypeName.GEOGRAPHY @@ -286,7 +288,7 @@ public boolean getBoolean(int columnIndex) throws SQLException { @Override public byte getByte(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getByte", "++enter++"); try { Object value = getObject(columnIndex); return this.bigQueryTypeCoercer.coerceTo(Byte.class, value); @@ -297,7 +299,7 @@ public byte getByte(int columnIndex) throws SQLException { @Override public short getShort(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getShort", "++enter++"); try { Object value = getObject(columnIndex); return this.bigQueryTypeCoercer.coerceTo(Short.class, value); @@ -308,7 +310,7 @@ public short getShort(int columnIndex) throws SQLException { @Override public int getInt(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getInt", "++enter++"); try { Object value = getObject(columnIndex); return this.bigQueryTypeCoercer.coerceTo(Integer.class, value); @@ -319,7 +321,7 @@ public int getInt(int columnIndex) throws SQLException { @Override public long getLong(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getLong", "++enter++"); try { Object value = getObject(columnIndex); return this.bigQueryTypeCoercer.coerceTo(Long.class, value); @@ -330,7 +332,7 @@ public long getLong(int columnIndex) throws SQLException { @Override public float getFloat(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getFloat", "++enter++"); try { Object value = getObject(columnIndex); return this.bigQueryTypeCoercer.coerceTo(Float.class, value); @@ -341,7 +343,7 @@ public float getFloat(int columnIndex) throws SQLException { @Override public double getDouble(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getDouble", "++enter++"); try { Object value = getObject(columnIndex); return this.bigQueryTypeCoercer.coerceTo(Double.class, value); @@ -352,7 +354,7 @@ public double getDouble(int columnIndex) throws SQLException { @Override public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getBigDecimal", "++enter++"); try { Object value = getObject(columnIndex); return this.bigQueryTypeCoercer.coerceTo(BigDecimal.class, value); @@ -363,7 +365,7 @@ public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException @Override public byte[] getBytes(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getBytes", "++enter++"); try { Object value = getObject(columnIndex); return this.bigQueryTypeCoercer.coerceTo(byte[].class, value); @@ -374,7 +376,7 @@ public byte[] getBytes(int columnIndex) throws SQLException { @Override public Date getDate(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getDate", "++enter++"); try { Object value = getObject(columnIndex); return this.bigQueryTypeCoercer.coerceTo(java.sql.Date.class, value); @@ -385,7 +387,7 @@ public Date getDate(int columnIndex) throws SQLException { @Override public Time getTime(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getTime", "++enter++"); StandardSQLTypeName type = getStandardSQLTypeName(columnIndex); if (type == StandardSQLTypeName.INT64) { throw createCoercionException(columnIndex, java.sql.Time.class, null); @@ -400,7 +402,7 @@ public Time getTime(int columnIndex) throws SQLException { @Override public Timestamp getTimestamp(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getTimestamp", "++enter++"); StandardSQLTypeName type = getStandardSQLTypeName(columnIndex); if (type == StandardSQLTypeName.INT64) { throw createCoercionException(columnIndex, java.sql.Timestamp.class, null); @@ -415,7 +417,7 @@ public Timestamp getTimestamp(int columnIndex) throws SQLException { @Override public BigDecimal getBigDecimal(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getBigDecimal", "++enter++"); try { Object value = getObject(columnIndex); return this.bigQueryTypeCoercer.coerceTo(BigDecimal.class, value); @@ -426,7 +428,7 @@ public BigDecimal getBigDecimal(int columnIndex) throws SQLException { @Override public Array getArray(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getArray", "++enter++"); try { return (Array) getObject(columnIndex); } catch (ClassCastException e) { @@ -436,27 +438,27 @@ public Array getArray(int columnIndex) throws SQLException { @Override public Blob getBlob(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getBlob", "++enter++"); byte[] value = getBytes(columnIndex); return new javax.sql.rowset.serial.SerialBlob(value); } @Override public Clob getClob(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getClob", "++enter++"); String value = getString(columnIndex); return new javax.sql.rowset.serial.SerialClob(value.toCharArray()); } @Override public Reader getCharacterStream(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getCharacterStream", "++enter++"); String value = getString(columnIndex); return value == null ? null : new StringReader(value); } private InputStream getInputStream(String value, java.nio.charset.Charset charset) { - LOG.finest("++enter++"); + LOG.finestTrace("getInputStream", "++enter++"); if (value == null) { return null; } @@ -465,26 +467,26 @@ private InputStream getInputStream(String value, java.nio.charset.Charset charse @Override public InputStream getAsciiStream(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getAsciiStream", "++enter++"); return getInputStream(getString(columnIndex), StandardCharsets.US_ASCII); } @Override public InputStream getUnicodeStream(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getUnicodeStream", "++enter++"); return getInputStream(getString(columnIndex), StandardCharsets.UTF_16LE); } @Override public InputStream getBinaryStream(int columnIndex) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getBinaryStream", "++enter++"); byte[] bytes = getBytes(columnIndex); return bytes == null ? null : new java.io.ByteArrayInputStream(bytes); } @Override public Date getDate(int columnIndex, Calendar cal) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getDate", "++enter++"); Date date = getDate(columnIndex); if (date == null || cal == null) { return null; @@ -495,7 +497,7 @@ public Date getDate(int columnIndex, Calendar cal) throws SQLException { @Override public Time getTime(int columnIndex, Calendar cal) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getTime", "++enter++"); Time time = getTime(columnIndex); if (time == null || cal == null) { return null; @@ -506,7 +508,7 @@ public Time getTime(int columnIndex, Calendar cal) throws SQLException { @Override public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("getTimestamp", "++enter++"); Timestamp timeStamp = getTimestamp(columnIndex); if (timeStamp == null || cal == null) { return null; @@ -517,7 +519,7 @@ public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException @Override public int findColumn(String columnLabel) throws SQLException { - LOG.finest("++enter++"); + LOG.finestTrace("findColumn", "++enter++"); return getColumnIndex(columnLabel); } diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseStruct.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseStruct.java index ce6afeada249..d516c938303d 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseStruct.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseStruct.java @@ -36,8 +36,8 @@ * attribute of the SQL structured type that it represents. */ abstract class BigQueryBaseStruct implements java.sql.Struct { - private static final BigQueryJdbcCustomLogger LOG = - new BigQueryJdbcCustomLogger(BigQueryBaseStruct.class.getName()); + private static final BigQueryJdbcResultSetLogger LOG = + BigQueryJdbcResultSetLogger.getLogger(BigQueryBaseStruct.class); abstract FieldList getSchema(); @@ -52,7 +52,7 @@ public final Object[] getAttributes(Map> map) throws SQLExcepti } static boolean isStruct(Field currentSchema) { - LOG.finest("++enter++"); + LOG.finestTrace("isStruct", "++enter++"); return currentSchema.getType().getStandardType() == STRUCT; } diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcCustomLogger.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcCustomLogger.java index 5195539439f2..75787850cbd8 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcCustomLogger.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcCustomLogger.java @@ -125,4 +125,14 @@ public void severe(String msg, Throwable thrown) { public void severe(String format, Throwable thrown, Object... args) { logWithCaller(Level.SEVERE, thrown, () -> String.format(format, args)); } + + @Override + public void finest(Supplier msgSupplier) { + logWithCaller(Level.FINEST, msgSupplier); + } + + @Override + public void fine(Supplier msgSupplier) { + logWithCaller(Level.FINE, msgSupplier); + } } diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcResultSetLogger.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcResultSetLogger.java new file mode 100644 index 000000000000..f93300e60813 --- /dev/null +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcResultSetLogger.java @@ -0,0 +1,180 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.bigquery.jdbc; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +/** + * Specialized high-performance logger for ResultSet and its related classes. Avoids expensive stack + * trace walking (caller inference) on hot-path logging calls. + */ +public class BigQueryJdbcResultSetLogger extends BigQueryJdbcCustomLogger { + private static final ConcurrentHashMap cache = + new ConcurrentHashMap<>(); + + private final String targetClassName; + private final String connectionId; + + public static BigQueryJdbcResultSetLogger getLogger(Class clazz) { + return getLogger(clazz, null); + } + + public static BigQueryJdbcResultSetLogger getLogger(Class clazz, String connectionId) { + if (connectionId == null) { + return cache.computeIfAbsent( + clazz.getName(), k -> new BigQueryJdbcResultSetLogger(clazz, null)); + } + return new BigQueryJdbcResultSetLogger(clazz, connectionId); + } + + public BigQueryJdbcResultSetLogger(Class clazz) { + this(clazz, null); + } + + public BigQueryJdbcResultSetLogger(Class clazz, String connectionId) { + super(clazz.getName()); + this.targetClassName = clazz.getName(); + this.connectionId = connectionId; + } + + @Override + public void log(LogRecord record) { + if (connectionId != null) { + Object[] existingParams = record.getParameters(); + if (existingParams == null || existingParams.length == 0) { + record.setParameters(new Object[] {connectionId}); + } else { + Object[] newParams = new Object[existingParams.length + 1]; + newParams[0] = connectionId; + System.arraycopy(existingParams, 0, newParams, 1, existingParams.length); + record.setParameters(newParams); + } + } + super.log(record); + } + + /** + * Log a message at Level.FINEST with predefined class name and method name to avoid stack trace + * parsing on the hot-path. + */ + public void finestTrace(String methodName, String msg) { + if (isLoggable(Level.FINEST)) { + logp(Level.FINEST, targetClassName, methodName, msg); + } + } + + /** Log a formatted message at Level.FINEST with predefined class name and method name. */ + public void finestTrace(String methodName, String format, Object... args) { + if (isLoggable(Level.FINEST)) { + logp(Level.FINEST, targetClassName, methodName, String.format(format, args)); + } + } + + /** Log a lazy message at Level.FINEST with predefined class name and method name. */ + public void finestTrace(String methodName, Supplier msgSupplier) { + if (isLoggable(Level.FINEST)) { + logp(Level.FINEST, targetClassName, methodName, msgSupplier); + } + } + + /** Log a message at Level.FINER with predefined class name and method name. */ + public void finerTrace(String methodName, String msg) { + if (isLoggable(Level.FINER)) { + logp(Level.FINER, targetClassName, methodName, msg); + } + } + + /** Log a formatted message at Level.FINER with predefined class name and method name. */ + public void finerTrace(String methodName, String format, Object... args) { + if (isLoggable(Level.FINER)) { + logp(Level.FINER, targetClassName, methodName, String.format(format, args)); + } + } + + /** Log a lazy message at Level.FINER with predefined class name and method name. */ + public void finerTrace(String methodName, Supplier msgSupplier) { + if (isLoggable(Level.FINER)) { + logp(Level.FINER, targetClassName, methodName, msgSupplier); + } + } + + /** Log a message at Level.FINE with predefined class name and method name. */ + public void fineTrace(String methodName, String msg) { + if (isLoggable(Level.FINE)) { + logp(Level.FINE, targetClassName, methodName, msg); + } + } + + /** Log a formatted message at Level.FINE with predefined class name and method name. */ + public void fineTrace(String methodName, String format, Object... args) { + if (isLoggable(Level.FINE)) { + logp(Level.FINE, targetClassName, methodName, String.format(format, args)); + } + } + + /** Log a lazy message at Level.FINE with predefined class name and method name. */ + public void fineTrace(String methodName, Supplier msgSupplier) { + if (isLoggable(Level.FINE)) { + logp(Level.FINE, targetClassName, methodName, msgSupplier); + } + } + + @Override + public void finest(String msg) { + if (isLoggable(Level.FINEST)) { + logp(Level.FINEST, targetClassName, "unknown", msg); + } + } + + @Override + public void finest(Supplier msgSupplier) { + if (isLoggable(Level.FINEST)) { + logp(Level.FINEST, targetClassName, "unknown", msgSupplier); + } + } + + @Override + public void finer(String msg) { + if (isLoggable(Level.FINER)) { + logp(Level.FINER, targetClassName, "unknown", msg); + } + } + + @Override + public void finer(Supplier msgSupplier) { + if (isLoggable(Level.FINER)) { + logp(Level.FINER, targetClassName, "unknown", msgSupplier); + } + } + + @Override + public void fine(String msg) { + if (isLoggable(Level.FINE)) { + logp(Level.FINE, targetClassName, "unknown", msg); + } + } + + @Override + public void fine(Supplier msgSupplier) { + if (isLoggable(Level.FINE)) { + logp(Level.FINE, targetClassName, "unknown", msgSupplier); + } + } +} diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcRootLogger.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcRootLogger.java index 32772521e9c2..06edb21c28bd 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcRootLogger.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcRootLogger.java @@ -88,6 +88,12 @@ public static Formatter getFormatter() { public String format(LogRecord record) { String date = DATE_FORMATTER.format(Instant.ofEpochMilli(record.getMillis())); String connectionId = BigQueryJdbcMdc.getConnectionId(); + if (connectionId == null || connectionId.isEmpty()) { + Object[] params = record.getParameters(); + if (params != null && params.length > 0 && params[0] instanceof String) { + connectionId = (String) params[0]; + } + } String connStr = (connectionId != null && !connectionId.isEmpty()) ? connectionId : "NO_CONN"; diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonArray.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonArray.java index 3b557a15a725..bc59192b84d7 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonArray.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonArray.java @@ -30,8 +30,8 @@ /** An implementation of {@link BigQueryBaseArray} used to represent Array values from Json data. */ @InternalApi class BigQueryJsonArray extends BigQueryBaseArray { - private static final BigQueryJdbcCustomLogger LOG = - new BigQueryJdbcCustomLogger(BigQueryJsonArray.class.getName()); + private static final BigQueryJdbcResultSetLogger LOG = + BigQueryJdbcResultSetLogger.getLogger(BigQueryJsonArray.class); private static final BigQueryTypeCoercer BIGQUERY_TYPE_COERCER = BigQueryTypeCoercionUtility.INSTANCE; private List values; @@ -44,7 +44,7 @@ class BigQueryJsonArray extends BigQueryBaseArray { @Override public Object getArray() { ensureValid(); - LOG.finest("++enter++"); + LOG.finestTrace("getArray", "++enter++"); if (this.values == null) { return null; } @@ -54,7 +54,7 @@ public Object getArray() { @Override public Object getArray(long index, int count) { ensureValid(); - LOG.finest("++enter++"); + LOG.finestTrace("getArray", "++enter++"); if (this.values == null) { return null; } @@ -65,7 +65,7 @@ public Object getArray(long index, int count) { @Override public ResultSet getResultSet() { ensureValid(); - LOG.finest("++enter++"); + LOG.finestTrace("getResultSet", "++enter++"); if (this.values == null) { return new BigQueryJsonResultSet(); } @@ -78,7 +78,7 @@ public ResultSet getResultSet() { @Override public ResultSet getResultSet(long index, int count) { ensureValid(); - LOG.finest("++enter++"); + LOG.finestTrace("getResultSet", "++enter++"); if (this.values == null) { return new BigQueryJsonResultSet(); } diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSet.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSet.java index 17198a753393..e2d99c34e6ca 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSet.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSet.java @@ -185,7 +185,7 @@ public boolean next() throws SQLException { public Object getObject(int columnIndex) throws SQLException { // columnIndex is SQL index starting at 1 checkClosed(); - LOG.finest("++enter++"); + LOG.finestTrace("getObject", "++enter++"); FieldValue value = getObjectInternal(columnIndex); if (value == null || value.isNull()) { return null; @@ -230,7 +230,7 @@ public Object getObject(int columnIndex) throws SQLException { */ private FieldValue getObjectInternal(int columnIndex) throws SQLException { checkClosed(); - LOG.finest("++enter++"); + LOG.finestTrace("getObjectInternal", "++enter++"); FieldValue value; if (this.isNested) { boolean validIndexForNestedResultSet = columnIndex == 1 || columnIndex == 2; @@ -272,7 +272,7 @@ private FieldValue getObjectInternal(int columnIndex) throws SQLException { @Override public void close() { - LOG.fine("Closing BigqueryJsonResultSet %s.", this); + LOG.fineTrace("close", () -> String.format("Closing BigqueryJsonResultSet %s.", this)); this.isClosed = true; if (ownedThreads != null) { for (Thread ownedThread : ownedThreads) { @@ -287,7 +287,7 @@ public void close() { @Override public boolean isBeforeFirst() throws SQLException { checkClosed(); - LOG.finest("++enter++"); + LOG.finestTrace("isBeforeFirst", "++enter++"); if (this.isNested) { return this.nestedRowIndex < this.fromIndex; } else { @@ -298,14 +298,14 @@ public boolean isBeforeFirst() throws SQLException { @Override public boolean isAfterLast() throws SQLException { checkClosed(); - LOG.finest("++enter++"); + LOG.finestTrace("isAfterLast", "++enter++"); return this.afterLast; } @Override public boolean isFirst() throws SQLException { checkClosed(); - LOG.finest("++enter++"); + LOG.finestTrace("isFirst", "++enter++"); if (this.isNested) { return this.nestedRowIndex == this.fromIndex; } else { @@ -316,7 +316,7 @@ public boolean isFirst() throws SQLException { @Override public boolean isLast() throws SQLException { checkClosed(); - LOG.finest("++enter++"); + LOG.finestTrace("isLast", "++enter++"); if (this.isNested) { return this.nestedRowIndex == this.toIndexExclusive - 1; } else { diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java index 35217f8e7117..504db6a1e097 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java @@ -30,8 +30,8 @@ */ @InternalApi class BigQueryJsonStruct extends BigQueryBaseStruct { - private static final BigQueryJdbcCustomLogger LOG = - new BigQueryJdbcCustomLogger(BigQueryJsonStruct.class.getName()); + private static final BigQueryJdbcResultSetLogger LOG = + BigQueryJdbcResultSetLogger.getLogger(BigQueryJsonStruct.class); private static final BigQueryTypeCoercer BIGQUERY_TYPE_COERCER = BigQueryTypeCoercionUtility.INSTANCE; @@ -51,7 +51,7 @@ FieldList getSchema() { @Override public Object[] getAttributes() { - LOG.finest("++enter++"); + LOG.finestTrace("getAttributes", "++enter++"); int size = schema.size(); Object[] attributes = (Object[]) Array.newInstance(Object.class, size); @@ -65,7 +65,7 @@ public Object[] getAttributes() { } private Object getValue(Field currentSchema, FieldValue currentValue) { - LOG.finest("++enter++"); + LOG.finestTrace("getValue", "++enter++"); if (isArray(currentSchema)) { return new BigQueryJsonArray(currentSchema, currentValue); } else if (isStruct(currentSchema)) { diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetFinalizers.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetFinalizers.java index 15a1cca349f1..97e04d858380 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetFinalizers.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetFinalizers.java @@ -22,8 +22,8 @@ @InternalApi class BigQueryResultSetFinalizers { - private static final BigQueryJdbcCustomLogger LOG = - new BigQueryJdbcCustomLogger(BigQueryResultSetFinalizers.class.getName()); + private static final BigQueryJdbcResultSetLogger LOG = + BigQueryJdbcResultSetLogger.getLogger(BigQueryResultSetFinalizers.class); @InternalApi static class ArrowResultSetFinalizer extends PhantomReference { @@ -39,7 +39,7 @@ public ArrowResultSetFinalizer( // Free resources. Remove all the hard refs public void finalizeResources() { - LOG.finest("++enter++"); + LOG.finestTrace("finalizeResources", "++enter++"); if (ownedThread != null && !ownedThread.isInterrupted()) { ownedThread.interrupt(); } @@ -60,7 +60,7 @@ public JsonResultSetFinalizer( // Free resources. Remove all the hard refs public void finalizeResources() { - LOG.finest("++enter++"); + LOG.finestTrace("finalizeResources", "++enter++"); if (ownedThreads != null) { for (Thread ownedThread : ownedThreads) { if (!ownedThread.isInterrupted()) { diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadata.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadata.java index eb088567a937..fc71394803b4 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadata.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryResultSetMetadata.java @@ -28,7 +28,8 @@ /** This class returns ResultSetMetadata for the JSON and the Arrow ResultSets */ class BigQueryResultSetMetadata implements ResultSetMetaData { - private final BigQueryJdbcCustomLogger LOG = new BigQueryJdbcCustomLogger(this.toString()); + private final BigQueryJdbcResultSetLogger LOG = + BigQueryJdbcResultSetLogger.getLogger(this.getClass()); private final FieldList schemaFieldList; private final Statement statement; private final int columnCount; @@ -36,7 +37,7 @@ class BigQueryResultSetMetadata implements ResultSetMetaData { private static final int DEFAULT_DISPLAY_SIZE = 50; private BigQueryResultSetMetadata(FieldList schemaFieldList, Statement statement) { - LOG.finest("++enter++"); + LOG.finestTrace("", "++enter++"); this.schemaFieldList = schemaFieldList; this.columnCount = schemaFieldList.size(); this.statement = statement; diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/PerConnectionFileHandler.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/PerConnectionFileHandler.java index 46cf647ef1f4..0b5279cf6bf0 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/PerConnectionFileHandler.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/PerConnectionFileHandler.java @@ -90,6 +90,12 @@ public void publish(LogRecord record) { } String connectionId = BigQueryJdbcMdc.getConnectionId(); + if (connectionId == null || connectionId.isEmpty()) { + Object[] params = record.getParameters(); + if (params != null && params.length > 0 && params[0] instanceof String) { + connectionId = (String) params[0]; + } + } FileHandler handler = defaultHandler; if (connectionId != null && !connectionId.isEmpty()) { diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcCustomLoggerTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcCustomLoggerTest.java index 56d05d6b56c0..3a286f0996cf 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcCustomLoggerTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcCustomLoggerTest.java @@ -17,6 +17,8 @@ package com.google.cloud.bigquery.jdbc; import static org.junit.jupiter.api.Assertions.assertEquals; +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 java.util.ArrayList; @@ -125,4 +127,184 @@ public void testLogWithException() { assertTrue(record.getMessage().contains("Error occurred: detail")); assertEquals(ex, record.getThrown()); } + + @Test + public void testResultSetLoggerTrace() { + BigQueryJdbcResultSetLogger rsLogger = + new BigQueryJdbcResultSetLogger(BigQueryBaseResultSet.class); + TestHandler rsHandler = new TestHandler(); + rsLogger.addHandler(rsHandler); + rsLogger.setLevel(Level.ALL); + + rsLogger.finestTrace("customMethod", "Hello finest trace message"); + + List records = rsHandler.getRecords(); + assertEquals(1, records.size()); + LogRecord record = records.get(0); + + assertEquals(Level.FINEST, record.getLevel()); + assertEquals("customMethod", record.getSourceMethodName()); + assertEquals(BigQueryBaseResultSet.class.getName(), record.getSourceClassName()); + assertEquals("Hello finest trace message", record.getMessage()); + } + + @Test + public void testResultSetLoggerTraceFormat() { + BigQueryJdbcResultSetLogger rsLogger = + new BigQueryJdbcResultSetLogger(BigQueryBaseResultSet.class); + TestHandler rsHandler = new TestHandler(); + rsLogger.addHandler(rsHandler); + rsLogger.setLevel(Level.ALL); + + rsLogger.finestTrace("formattedMethod", "Value: %s, Code: %d", "abc", 123); + + List records = rsHandler.getRecords(); + assertEquals(1, records.size()); + LogRecord record = records.get(0); + + assertEquals(Level.FINEST, record.getLevel()); + assertEquals("formattedMethod", record.getSourceMethodName()); + assertEquals(BigQueryBaseResultSet.class.getName(), record.getSourceClassName()); + assertEquals("Value: abc, Code: 123", record.getMessage()); + } + + @Test + public void testResultSetLoggerStandardMethods() { + BigQueryJdbcResultSetLogger rsLogger = + new BigQueryJdbcResultSetLogger(BigQueryBaseResultSet.class); + TestHandler rsHandler = new TestHandler(); + rsLogger.addHandler(rsHandler); + rsLogger.setLevel(Level.ALL); + + rsLogger.finest("Finest msg"); + rsLogger.finer("Finer msg"); + rsLogger.fine("Fine msg"); + + List records = rsHandler.getRecords(); + assertEquals(3, records.size()); + + LogRecord r1 = records.get(0); + assertEquals(Level.FINEST, r1.getLevel()); + assertEquals("unknown", r1.getSourceMethodName()); + assertEquals(BigQueryBaseResultSet.class.getName(), r1.getSourceClassName()); + assertEquals("Finest msg", r1.getMessage()); + + LogRecord r2 = records.get(1); + assertEquals(Level.FINER, r2.getLevel()); + assertEquals("unknown", r2.getSourceMethodName()); + assertEquals("Finer msg", r2.getMessage()); + + LogRecord r3 = records.get(2); + assertEquals(Level.FINE, r3.getLevel()); + assertEquals("unknown", r3.getSourceMethodName()); + assertEquals("Fine msg", r3.getMessage()); + } + + @Test + public void testCustomLoggerSupplierMethods() { + logger.fine(() -> "Lazy fine message"); + + List records = testHandler.getRecords(); + assertEquals(1, records.size()); + LogRecord record = records.get(0); + + assertEquals(Level.FINE, record.getLevel()); + assertEquals("testCustomLoggerSupplierMethods", record.getSourceMethodName()); + assertEquals(BigQueryJdbcCustomLoggerTest.class.getName(), record.getSourceClassName()); + assertEquals("Lazy fine message", record.getMessage()); + } + + @Test + public void testResultSetLoggerSupplierMethods() { + BigQueryJdbcResultSetLogger rsLogger = + new BigQueryJdbcResultSetLogger(BigQueryBaseResultSet.class); + TestHandler rsHandler = new TestHandler(); + rsLogger.addHandler(rsHandler); + rsLogger.setLevel(Level.ALL); + + rsLogger.finest(() -> "Lazy finest RS message"); + + List records = rsHandler.getRecords(); + assertEquals(1, records.size()); + LogRecord record = records.get(0); + + assertEquals(Level.FINEST, record.getLevel()); + assertEquals("unknown", record.getSourceMethodName()); + assertEquals(BigQueryBaseResultSet.class.getName(), record.getSourceClassName()); + assertEquals("Lazy finest RS message", record.getMessage()); + } + + @Test + public void testSupplierNotEvaluatedWhenDisabled() { + logger.setLevel(Level.INFO); // Disables FINE, FINER, FINEST + + boolean[] evaluated = {false}; + logger.fine( + () -> { + evaluated[0] = true; + return "This should not be evaluated"; + }); + + assertEquals(0, testHandler.getRecords().size()); + assertEquals(false, evaluated[0]); + } + + @Test + public void testResultSetLoggerSupplierTraceMethods() { + BigQueryJdbcResultSetLogger rsLogger = + new BigQueryJdbcResultSetLogger(BigQueryBaseResultSet.class); + TestHandler rsHandler = new TestHandler(); + rsLogger.addHandler(rsHandler); + rsLogger.setLevel(Level.ALL); + + rsLogger.finestTrace("customTraceMethod", () -> "Lazy finest RS trace message"); + + List records = rsHandler.getRecords(); + assertEquals(1, records.size()); + LogRecord record = records.get(0); + + assertEquals(Level.FINEST, record.getLevel()); + assertEquals("customTraceMethod", record.getSourceMethodName()); + assertEquals(BigQueryBaseResultSet.class.getName(), record.getSourceClassName()); + assertEquals("Lazy finest RS trace message", record.getMessage()); + } + + @Test + public void testResultSetLoggerCachingBehavior() { + // Global logger (connectionId is null) should be cached + BigQueryJdbcResultSetLogger globalLogger1 = + BigQueryJdbcResultSetLogger.getLogger(BigQueryBaseResultSet.class); + BigQueryJdbcResultSetLogger globalLogger2 = + BigQueryJdbcResultSetLogger.getLogger(BigQueryBaseResultSet.class); + assertSame(globalLogger1, globalLogger2); + + // Connection-specific loggers should not be cached globally + BigQueryJdbcResultSetLogger connLogger1 = + BigQueryJdbcResultSetLogger.getLogger(BigQueryBaseResultSet.class, "conn-123"); + BigQueryJdbcResultSetLogger connLogger2 = + BigQueryJdbcResultSetLogger.getLogger(BigQueryBaseResultSet.class, "conn-123"); + assertNotSame(connLogger1, connLogger2); + } + + @Test + public void testResultSetLoggerParameterPreservation() { + BigQueryJdbcResultSetLogger rsLogger = + new BigQueryJdbcResultSetLogger(BigQueryBaseResultSet.class, "conn-123"); + TestHandler rsHandler = new TestHandler(); + rsLogger.addHandler(rsHandler); + rsLogger.setLevel(Level.ALL); + + LogRecord record = new LogRecord(Level.INFO, "Processing job {0}"); + record.setParameters(new Object[] {"job-999"}); + rsLogger.log(record); + + List records = rsHandler.getRecords(); + assertEquals(1, records.size()); + LogRecord loggedRecord = records.get(0); + + Object[] params = loggedRecord.getParameters(); + assertEquals(2, params.length); + assertEquals("conn-123", params[0]); + assertEquals("job-999", params[1]); + } }