Skip to content

Commit 293f18f

Browse files
authored
feat(bqjdbc): implement Per connection logging with Context proxy (#13001)
b/501081433 1. **Foundational MDC & Global File Handler Routing** Simplifies the BigQueryJdbcMdc ThreadLocal context slots, sets up "Jdbc-global" fallback file routing, and adds isFileLoggingEnabled() to the root logger. 2. **Dynamic Context Proxy Wrapper & Connection Wrap** Implements the BigQueryJdbcContextProxy dynamic proxy interceptor, delegating standard wrappers, and wrapping all new JDBC entries in BigQueryDriver. 3. **Statement Modifications & Connection Pool unwrapping** Adapts Statements and Connection ID builders to utilize the dynamic proxy, and updates connection pool managers to safely extract raw connection properties via standard unwrap calls. 4. **0%-Overhead Raw ResultSet & Metadata Routing** Completely un-proxies ResultSet to achieve 0% query execution overhead, while implementing targeted ResultSetMetaData proxy wrapping inside getMetaData() to keep metadata queries connection-routed. 5. **Decoupled Exceptions & Clean Integration Tests** Decouples all custom exception constructors to eliminate double-logging, converts all 17 integration test class casts to standard unwrap calls, and migrates all deprecated github_timeline public dataset queries to the active shakespeare dataset.
1 parent 2312aae commit 293f18f

30 files changed

Lines changed: 878 additions & 527 deletions

java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/exception/BigQueryConversionException.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,14 @@
1616

1717
package com.google.cloud.bigquery.exception;
1818

19-
import com.google.cloud.bigquery.jdbc.BigQueryJdbcCustomLogger;
2019
import java.sql.SQLException;
2120

2221
/**
2322
* Exception for errors that occur when the driver cannot convert a value from one type to another.
2423
*/
2524
public class BigQueryConversionException extends SQLException {
26-
private static final BigQueryJdbcCustomLogger LOG =
27-
new BigQueryJdbcCustomLogger(BigQueryConversionException.class.getName());
2825

2926
public BigQueryConversionException(String message, Throwable cause) {
3027
super(BigQueryJdbcExceptionUtils.formatMessage(message, cause), cause);
31-
LOG.severe(this.getMessage(), this);
3228
}
3329
}

java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/exception/BigQueryJdbcCoercionException.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,13 @@
1717
package com.google.cloud.bigquery.exception;
1818

1919
import com.google.api.core.InternalApi;
20-
import com.google.cloud.bigquery.jdbc.BigQueryJdbcCustomLogger;
2120

2221
/**
2322
* Thrown to indicate that the coercion was attempted but couldn't be performed successfully because
2423
* of some error.
2524
*/
2625
@InternalApi
2726
public class BigQueryJdbcCoercionException extends RuntimeException {
28-
private static final BigQueryJdbcCustomLogger LOG =
29-
new BigQueryJdbcCustomLogger(BigQueryJdbcCoercionException.class.getName());
3027

3128
/**
3229
* Construct a new exception with the specified cause.
@@ -35,6 +32,5 @@ public class BigQueryJdbcCoercionException extends RuntimeException {
3532
*/
3633
public BigQueryJdbcCoercionException(Exception cause) {
3734
super(BigQueryJdbcExceptionUtils.formatMessage("Coercion error", cause), cause);
38-
LOG.severe(this.getMessage(), this);
3935
}
4036
}

java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/exception/BigQueryJdbcCoercionNotFoundException.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,13 @@
1717
package com.google.cloud.bigquery.exception;
1818

1919
import com.google.api.core.InternalApi;
20-
import com.google.cloud.bigquery.jdbc.BigQueryJdbcCustomLogger;
2120

2221
/**
2322
* Thrown to indicate that the current TypeCoercer can not perform the coercion as the Coercion
2423
* implementation is not registered for the mentioned source and target type.
2524
*/
2625
@InternalApi
2726
public class BigQueryJdbcCoercionNotFoundException extends RuntimeException {
28-
private static final BigQueryJdbcCustomLogger LOG =
29-
new BigQueryJdbcCustomLogger(BigQueryJdbcCoercionNotFoundException.class.getName());
3027

3128
/**
3229
* Construct a new exception.
@@ -39,6 +36,5 @@ public BigQueryJdbcCoercionNotFoundException(Class<?> source, Class<?> target) {
3936
String.format(
4037
"Coercion not found for [%s -> %s] conversion",
4138
source.getCanonicalName(), target.getCanonicalName()));
42-
LOG.severe(this.getMessage(), this);
4339
}
4440
}

java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/exception/BigQueryJdbcException.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,9 @@
1717
package com.google.cloud.bigquery.exception;
1818

1919
import com.google.cloud.bigquery.BigQueryException;
20-
import com.google.cloud.bigquery.jdbc.BigQueryJdbcCustomLogger;
2120
import java.sql.SQLException;
2221

2322
public class BigQueryJdbcException extends SQLException {
24-
private static final BigQueryJdbcCustomLogger LOG =
25-
new BigQueryJdbcCustomLogger(BigQueryJdbcException.class.getName());
2623
private BigQueryException bigQueryException = null;
2724

2825
/**
@@ -32,7 +29,6 @@ public class BigQueryJdbcException extends SQLException {
3229
*/
3330
public BigQueryJdbcException(String message) {
3431
super(message);
35-
LOG.severe(message, this);
3632
}
3733

3834
/**
@@ -42,7 +38,6 @@ public BigQueryJdbcException(String message) {
4238
*/
4339
public BigQueryJdbcException(InterruptedException ex) {
4440
super(ex);
45-
LOG.severe(ex.getMessage(), this);
4641
}
4742

4843
/**
@@ -54,7 +49,6 @@ public BigQueryJdbcException(InterruptedException ex) {
5449
public BigQueryJdbcException(String message, BigQueryException ex) {
5550
super(BigQueryJdbcExceptionUtils.formatMessage(message, ex), ex);
5651
this.bigQueryException = ex;
57-
LOG.severe(this.getMessage(), this);
5852
}
5953

6054
/**
@@ -65,7 +59,6 @@ public BigQueryJdbcException(String message, BigQueryException ex) {
6559
*/
6660
public BigQueryJdbcException(String message, Throwable cause) {
6761
super(BigQueryJdbcExceptionUtils.formatMessage(message, cause), cause);
68-
LOG.severe(this.getMessage(), this);
6962
}
7063

7164
/**
@@ -76,7 +69,6 @@ public BigQueryJdbcException(String message, Throwable cause) {
7669
*/
7770
public BigQueryJdbcException(Throwable cause) {
7871
super(cause);
79-
LOG.severe(cause.getMessage(), this);
8072
}
8173

8274
public BigQueryException getBigQueryException() {

java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/exception/BigQueryJdbcRuntimeException.java

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,15 @@
1616

1717
package com.google.cloud.bigquery.exception;
1818

19-
import com.google.cloud.bigquery.jdbc.BigQueryJdbcCustomLogger;
20-
2119
public class BigQueryJdbcRuntimeException extends RuntimeException {
2220

23-
private static final BigQueryJdbcCustomLogger LOG =
24-
new BigQueryJdbcCustomLogger(BigQueryJdbcRuntimeException.class.getName());
25-
2621
/**
2722
* Constructs a new BigQueryJdbcRuntimeException with the given message.
2823
*
2924
* @param message The detail message.
3025
*/
3126
public BigQueryJdbcRuntimeException(String message) {
3227
super(message);
33-
LOG.severe(message, this);
3428
}
3529

3630
/**
@@ -40,7 +34,6 @@ public BigQueryJdbcRuntimeException(String message) {
4034
*/
4135
public BigQueryJdbcRuntimeException(Throwable ex) {
4236
super(ex);
43-
LOG.severe(ex.getMessage(), this);
4437
}
4538

4639
/**
@@ -50,12 +43,10 @@ public BigQueryJdbcRuntimeException(Throwable ex) {
5043
* @param ex Throwable to be thrown.
5144
*/
5245
public BigQueryJdbcRuntimeException(String message, InterruptedException ex) {
53-
super(BigQueryJdbcExceptionUtils.formatMessage(message, ex), ex);
54-
LOG.severe(this.getMessage(), this);
46+
super(message, ex);
5547
}
5648

5749
public BigQueryJdbcRuntimeException(String message, Throwable ex) {
5850
super(BigQueryJdbcExceptionUtils.formatMessage(message, ex), ex);
59-
LOG.severe(this.getMessage(), this);
6051
}
6152
}

java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/exception/BigQueryJdbcSqlFeatureNotSupportedException.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,9 @@
1717
package com.google.cloud.bigquery.exception;
1818

1919
import com.google.cloud.bigquery.BigQueryException;
20-
import com.google.cloud.bigquery.jdbc.BigQueryJdbcCustomLogger;
2120
import java.sql.SQLFeatureNotSupportedException;
2221

2322
public class BigQueryJdbcSqlFeatureNotSupportedException extends SQLFeatureNotSupportedException {
24-
private static final BigQueryJdbcCustomLogger LOG =
25-
new BigQueryJdbcCustomLogger(BigQueryJdbcSqlFeatureNotSupportedException.class.getName());
2623

2724
/**
2825
* Constructs a new BigQueryJdbcSqlFeatureNotSupportedException with the given message.
@@ -31,7 +28,6 @@ public class BigQueryJdbcSqlFeatureNotSupportedException extends SQLFeatureNotSu
3128
*/
3229
public BigQueryJdbcSqlFeatureNotSupportedException(String message) {
3330
super(message);
34-
LOG.severe(message, this);
3531
}
3632

3733
/**
@@ -41,6 +37,5 @@ public BigQueryJdbcSqlFeatureNotSupportedException(String message) {
4137
*/
4238
public BigQueryJdbcSqlFeatureNotSupportedException(BigQueryException ex) {
4339
super(ex);
44-
LOG.severe(ex.getMessage(), this);
4540
}
4641
}

java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/exception/BigQueryJdbcSqlSyntaxErrorException.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package com.google.cloud.bigquery.exception;
1818

1919
import com.google.cloud.bigquery.BigQueryException;
20-
import com.google.cloud.bigquery.jdbc.BigQueryJdbcCustomLogger;
2120
import java.sql.SQLSyntaxErrorException;
2221

2322
/**
@@ -26,8 +25,6 @@
2625
* rules.
2726
*/
2827
public class BigQueryJdbcSqlSyntaxErrorException extends SQLSyntaxErrorException {
29-
private static final BigQueryJdbcCustomLogger LOG =
30-
new BigQueryJdbcCustomLogger(BigQueryJdbcSqlSyntaxErrorException.class.getName());
3128

3229
/**
3330
* Constructs a new BigQueryJdbcSqlSyntaxErrorException from BigQueryException
@@ -36,11 +33,9 @@ public class BigQueryJdbcSqlSyntaxErrorException extends SQLSyntaxErrorException
3633
*/
3734
public BigQueryJdbcSqlSyntaxErrorException(BigQueryException ex) {
3835
super(ex.getMessage(), "Incorrect SQL syntax.");
39-
LOG.severe(ex.getMessage(), this);
4036
}
4137

4238
public BigQueryJdbcSqlSyntaxErrorException(String message, BigQueryException ex) {
4339
super(BigQueryJdbcExceptionUtils.formatMessage(message, ex), ex);
44-
LOG.severe(this.getMessage(), this);
4540
}
4641
}

java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,8 @@ public boolean next() throws SQLException {
215215
checkClosed();
216216
if (this.isNested) {
217217
if (this.currentNestedBatch == null || this.currentNestedBatch.getNestedRecords() == null) {
218-
IllegalStateException ex =
219-
new IllegalStateException(
220-
"currentNestedBatch/JsonStringArrayList can not be null working with the nested record");
221-
LOG.severe(ex.getMessage(), ex);
222-
throw ex;
218+
throw new IllegalStateException(
219+
"currentNestedBatch/JsonStringArrayList can not be null working with the nested record");
223220
}
224221
if (this.nestedRowIndex < (this.toIndexExclusive - 1)) {
225222
/* Check if there's a next record in the array which can be read */

java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646

4747
public abstract class BigQueryBaseResultSet extends BigQueryNoOpsResultSet
4848
implements BigQueryResultSet {
49-
protected final BigQueryJdbcCustomLogger LOG = new BigQueryJdbcCustomLogger(this.toString());
49+
protected final BigQueryJdbcCustomLogger LOG =
50+
new BigQueryJdbcCustomLogger(this.getClass().getName());
5051
private BigQuery bigQuery;
5152
private JobId jobId;
5253
private String queryId;
@@ -107,6 +108,25 @@ public void close() {
107108
}
108109
}
109110

111+
protected SQLException logAndCreateException(SQLException ex) {
112+
if (this.statement == null) {
113+
return ex;
114+
}
115+
try (BigQueryJdbcMdc.MdcCloseable mdc =
116+
BigQueryJdbcMdc.registerInstance(this.statement.connectionId)) {
117+
LOG.severe(ex.getMessage(), ex);
118+
}
119+
return ex;
120+
}
121+
122+
protected SQLException logAndCreateException(String message) {
123+
return logAndCreateException(new SQLException(message));
124+
}
125+
126+
protected SQLException logAndCreateException(String message, Throwable cause) {
127+
return logAndCreateException(new SQLException(message, cause));
128+
}
129+
110130
protected SQLException createCoercionException(
111131
int columnIndex, Class<?> targetClass, Exception cause) throws SQLException {
112132
checkClosed();
@@ -115,29 +135,31 @@ protected SQLException createCoercionException(
115135

116136
if (isNested) {
117137
if (columnIndex == 1) {
118-
return new BigQueryConversionException(
119-
String.format("Cannot convert index column to type %s.", targetClass.getSimpleName()),
120-
cause);
138+
return logAndCreateException(
139+
new BigQueryConversionException(
140+
String.format(
141+
"Cannot convert index column to type %s.", targetClass.getSimpleName()),
142+
cause));
121143
} else if (columnIndex == 2) {
122144
Field arrayField = this.schema.getFields().get(0);
123145
type = arrayField.getType().getStandardType();
124146
typeName = type.name();
125147
} else {
126-
SQLException ex =
148+
throw logAndCreateException(
127149
new SQLException(
128-
"For a nested ResultSet from an Array, columnIndex must be 1 or 2.", cause);
129-
LOG.severe(ex.getMessage(), ex);
130-
throw ex;
150+
"For a nested ResultSet from an Array, columnIndex must be 1 or 2.", cause));
131151
}
132152
} else {
133153
Field field = this.schemaFieldList.get(columnIndex - 1);
134154
type = field.getType().getStandardType();
135155
typeName = type.name();
136156
}
137-
return new BigQueryConversionException(
138-
String.format(
139-
"Cannot convert value of type %s to type %s.", typeName, targetClass.getSimpleName()),
140-
cause);
157+
return logAndCreateException(
158+
new BigQueryConversionException(
159+
String.format(
160+
"Cannot convert value of type %s to type %s.",
161+
typeName, targetClass.getSimpleName()),
162+
cause));
141163
}
142164

143165
private StandardSQLTypeName getStandardSQLTypeName(int columnIndex) throws SQLException {
@@ -147,25 +169,19 @@ private StandardSQLTypeName getStandardSQLTypeName(int columnIndex) throws SQLEx
147169
return StandardSQLTypeName.INT64;
148170
} else if (columnIndex == 2) {
149171
if (this.schema == null || this.schema.getFields().isEmpty()) {
150-
SQLException ex = new SQLException("Schema not available for nested result set.");
151-
LOG.severe("Schema not available for nested result set.", ex);
152-
throw ex;
172+
throw logAndCreateException("Schema not available for nested result set.");
153173
}
154174
Field arrayField = this.schema.getFields().get(0);
155175
return arrayField.getType().getStandardType();
156176
} else {
157-
SQLException ex =
158-
new SQLException("For a nested ResultSet from an Array, columnIndex must be 1 or 2.");
159-
LOG.severe("For a nested ResultSet from an Array, columnIndex must be 1 or 2.", ex);
160-
throw ex;
177+
throw logAndCreateException(
178+
"For a nested ResultSet from an Array, columnIndex must be 1 or 2.");
161179
}
162180
} else {
163181
if (this.schemaFieldList == null
164182
|| columnIndex > this.schemaFieldList.size()
165183
|| columnIndex < 1) {
166-
SQLException ex = new SQLException("Invalid column index: " + columnIndex);
167-
LOG.severe("Invalid column index: " + columnIndex, ex);
168-
throw ex;
184+
throw logAndCreateException("Invalid column index: " + columnIndex);
169185
}
170186
Field field = this.schemaFieldList.get(columnIndex - 1);
171187
return field.getType().getStandardType();
@@ -185,11 +201,16 @@ public boolean wasNull() throws SQLException {
185201
@Override
186202
public ResultSetMetaData getMetaData() throws SQLException {
187203
checkClosed();
188-
if (this.isNested) {
189-
return BigQueryResultSetMetadata.of(this.schemaFieldList, this.statement);
190-
} else {
191-
return BigQueryResultSetMetadata.of(this.schema.getFields(), this.statement);
204+
String connectionId = this.statement != null ? this.statement.connectionId : null;
205+
ResultSetMetaData metaData;
206+
try (BigQueryJdbcMdc.MdcCloseable mdc = BigQueryJdbcMdc.registerInstance(connectionId)) {
207+
if (this.isNested) {
208+
metaData = BigQueryResultSetMetadata.of(this.schemaFieldList, this.statement);
209+
} else {
210+
metaData = BigQueryResultSetMetadata.of(this.schema.getFields(), this.statement);
211+
}
192212
}
213+
return BigQueryJdbcContextProxy.wrap(metaData, ResultSetMetaData.class, connectionId);
193214
}
194215

195216
@Override
@@ -227,9 +248,7 @@ protected int getColumnIndex(String columnLabel) throws SQLException {
227248
LOG.finest("++enter++");
228249
checkClosed();
229250
if (columnLabel == null) {
230-
SQLException ex = new SQLException("Column label cannot be null");
231-
LOG.severe(ex.getMessage(), ex);
232-
throw ex;
251+
throw logAndCreateException("Column label cannot be null");
233252
}
234253
// use schema to get the column index, add 1 for SQL index
235254
return this.schemaFieldList.getIndex(columnLabel) + 1;

0 commit comments

Comments
 (0)