From e0dd6607a1b94dc2513e06c977b79c3fd7f8be2c Mon Sep 17 00:00:00 2001 From: Keshav Dandeva Date: Tue, 5 May 2026 01:42:58 +0000 Subject: [PATCH 01/11] feat(bqjdbc): Implement Correlated OpenTelemetry Logging Bridge --- .../google-cloud-bigquery-jdbc/pom.xml | 5 + .../bigquery/jdbc/BigQueryConnection.java | 9 ++ .../cloud/bigquery/jdbc/BigQueryDriver.java | 7 +- .../bigquery/jdbc/BigQueryJdbcRootLogger.java | 5 +- .../jdbc/OpenTelemetryJulHandler.java | 132 ++++++++++++++++++ .../jdbc/OpenTelemetryJulHandlerTest.java | 92 ++++++++++++ 6 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java create mode 100644 java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java diff --git a/java-bigquery/google-cloud-bigquery-jdbc/pom.xml b/java-bigquery/google-cloud-bigquery-jdbc/pom.xml index 74f3df2fff9f..b7e17dea02f8 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/pom.xml +++ b/java-bigquery/google-cloud-bigquery-jdbc/pom.xml @@ -182,6 +182,11 @@ google-cloud-bigquerystorage 3.28.0-SNAPSHOT + + com.google.cloud + google-cloud-logging + 3.33.0-SNAPSHOT + com.google.http-client google-http-client-apache-v5 diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnection.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnection.java index 6f17d3d6d195..d7fd3d8d19d8 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnection.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnection.java @@ -64,6 +64,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; /** * An implementation of {@link java.sql.Connection} for establishing a connection with BigQuery and @@ -1056,6 +1057,14 @@ private BigQuery getBigQueryConnection() { OpenTelemetry openTelemetry = BigQueryJdbcOpenTelemetry.getOpenTelemetry( this.enableGcpTraceExporter, this.enableGcpLogExporter, this.customOpenTelemetry); + + if (this.enableGcpLogExporter || this.customOpenTelemetry != null) { + OpenTelemetryJulHandler otelHandler = + new OpenTelemetryJulHandler( + null, openTelemetry, this.enableGcpLogExporter, this.connectionId); + Logger.getLogger("com.google.cloud.bigquery").addHandler(otelHandler); + } + if (this.enableGcpTraceExporter || this.customOpenTelemetry != null) { this.tracer = BigQueryJdbcOpenTelemetry.getTracer(openTelemetry); bigQueryOptions.setOpenTelemetryTracer(this.tracer); diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDriver.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDriver.java index a83bdc5093e6..5312e3eb0d70 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDriver.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDriver.java @@ -158,7 +158,12 @@ public Connection connect(String url, Properties info) throws SQLException { logPath = System.getenv(BigQueryJdbcUrlUtility.LOG_PATH_ENV_VAR); } if (logPath == null) { - logPath = BigQueryJdbcUrlUtility.DEFAULT_LOG_PATH; + // Cloud-Only Mode: Suppress local file creation if GCP log exporter is enabled + if (ds.getEnableGcpLogExporter() && logLevel != Level.OFF) { + logPath = null; + } else { + logPath = BigQueryJdbcUrlUtility.DEFAULT_LOG_PATH; + } } BigQueryJdbcRootLogger.setLevel(logLevel, logPath); 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..71d48f0de3dc 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 @@ -138,7 +138,9 @@ public static Logger getRootLogger() { public static void setLevel(Level level, String logPath) throws IOException { if (level != Level.OFF) { - setPath(logPath, level); + if (logPath != null) { + setPath(logPath, level); + } logger.setLevel(level); } else { for (Handler h : logger.getHandlers()) { @@ -146,6 +148,7 @@ public static void setLevel(Level level, String logPath) throws IOException { logger.removeHandler(h); } fileHandler = null; + logger.setLevel(Level.OFF); } } diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java new file mode 100644 index 000000000000..97408d3de471 --- /dev/null +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java @@ -0,0 +1,132 @@ +/* + * 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 com.google.cloud.logging.Logging; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.baggage.Baggage; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.logs.Logger; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.context.Context; +import java.time.Instant; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +/** + * Custom logging handler that bridges java.util.logging records to OpenTelemetry or Google Cloud + * Logging. Extracts TraceId, SpanId, and Connection UUID from context. + */ +public class OpenTelemetryJulHandler extends Handler { + + private final Logging loggingClient; + private final OpenTelemetry openTelemetry; + private final boolean isGcpFallback; + private final String expectedConnectionId; + + public OpenTelemetryJulHandler( + Logging loggingClient, + OpenTelemetry openTelemetry, + boolean isGcpFallback, + String expectedConnectionId) { + this.loggingClient = loggingClient; + this.openTelemetry = openTelemetry; + this.isGcpFallback = isGcpFallback; + this.expectedConnectionId = expectedConnectionId; + } + + @Override + public void publish(LogRecord record) { + if (!isLoggable(record)) { + return; + } + + Context context = Context.current(); + SpanContext spanContext = Span.fromContext(context).getSpanContext(); + + String traceId = spanContext.isValid() ? spanContext.getTraceId() : null; + String spanId = spanContext.isValid() ? spanContext.getSpanId() : null; + + // Extract connection ID from baggage + String connectionId = Baggage.fromContext(context).getEntryValue("jdbc.connection_id"); + + // Fallback to MDC if not in baggage (if MDC is available and used) + if (connectionId == null) { + connectionId = BigQueryJdbcMdc.getConnectionId(); + } + + if (expectedConnectionId != null && !expectedConnectionId.equals(connectionId)) { + return; + } + + if (isGcpFallback && loggingClient != null) { + publishToGcp(record, traceId, spanId, connectionId); + } else if (openTelemetry != null) { + publishToOTel(record, traceId, spanId, connectionId); + } + } + + private void publishToGcp(LogRecord record, String traceId, String spanId, String connectionId) { + // TODO(b/491238299) + } + + private void publishToOTel(LogRecord record, String traceId, String spanId, String connectionId) { + if (openTelemetry == null) { + return; + } + + String loggerName = record.getLoggerName(); + Logger logger = + openTelemetry + .getLogsBridge() + .get(loggerName != null ? loggerName : "com.google.cloud.bigquery.jdbc"); + + logger + .logRecordBuilder() + .setBody(record.getMessage()) + .setSeverity(mapSeverity(record.getLevel())) + .setTimestamp(Instant.ofEpochMilli(record.getMillis())) + .setContext(Context.current()) + .setAttribute( + AttributeKey.stringKey("jdbc.connection_id"), connectionId != null ? connectionId : "") + .emit(); + } + + private Severity mapSeverity(Level level) { + if (level == Level.SEVERE) return Severity.ERROR; + if (level == Level.WARNING) return Severity.WARN; + if (level == Level.INFO) return Severity.INFO; + if (level == Level.CONFIG) return Severity.INFO; + if (level == Level.FINE) return Severity.DEBUG; + if (level == Level.FINER) return Severity.TRACE; + if (level == Level.FINEST) return Severity.TRACE; + return Severity.TRACE; + } + + @Override + public void flush() { + // TODO(b/491238299) - needed for publishToGcp + } + + @Override + public void close() throws SecurityException { + // TODO(b/491238299) - needed for publishToGcp + } +} diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java new file mode 100644 index 000000000000..246d7cde0735 --- /dev/null +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java @@ -0,0 +1,92 @@ +/* + * 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 static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.opentelemetry.api.baggage.Baggage; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.context.Scope; +import io.opentelemetry.sdk.logs.data.LogRecordData; +import io.opentelemetry.sdk.testing.junit5.OpenTelemetryExtension; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class OpenTelemetryJulHandlerTest { + + @RegisterExtension + static final OpenTelemetryExtension otelTesting = OpenTelemetryExtension.create(); + + @Test + public void testPublishToOTel() { + OpenTelemetryJulHandler handler = + new OpenTelemetryJulHandler(null, otelTesting.getOpenTelemetry(), false, null); + + LogRecord record = new LogRecord(Level.INFO, "Test message"); + record.setLoggerName("test.logger"); + record.setMillis(System.currentTimeMillis()); + + handler.publish(record); + + List logs = otelTesting.getLogRecords(); + assertEquals(1, logs.size()); + LogRecordData log = logs.get(0); + assertEquals("Test message", log.getBody().asString()); + assertEquals("test.logger", log.getInstrumentationScopeInfo().getName()); + assertEquals(Severity.INFO, log.getSeverity()); + } + + @Test + public void testPublishToOTelWithFiltering() { + // Handler expects "correct-uuid" + OpenTelemetryJulHandler handler = + new OpenTelemetryJulHandler(null, otelTesting.getOpenTelemetry(), false, "correct-uuid"); + + // Simulate log with NO connection ID + LogRecord record = new LogRecord(Level.INFO, "Test message"); + record.setLoggerName("test.logger"); + + handler.publish(record); + + List logs = otelTesting.getLogRecords(); + assertTrue(logs.isEmpty()); // Should be filtered out + } + + @Test + public void testPublishToOTelWithBaggage() { + OpenTelemetryJulHandler handler = + new OpenTelemetryJulHandler(null, otelTesting.getOpenTelemetry(), false, null); + + Baggage baggage = Baggage.builder().put("jdbc.connection_id", "test-uuid").build(); + try (Scope scope = baggage.makeCurrent()) { + LogRecord record = new LogRecord(Level.INFO, "Test message"); + record.setLoggerName("test.logger"); + handler.publish(record); + + List logs = otelTesting.getLogRecords(); + assertEquals(1, logs.size()); + LogRecordData log = logs.get(0); + assertEquals( + "test-uuid", log.getAttributes().get(AttributeKey.stringKey("jdbc.connection_id"))); + } + } +} From 8d72c8aaa2d56ceeeaa042f5c85c6ab26cea0506 Mon Sep 17 00:00:00 2001 From: Keshav Dandeva Date: Tue, 5 May 2026 02:08:17 +0000 Subject: [PATCH 02/11] chore: add gpc logging handler --- .../jdbc/OpenTelemetryJulHandler.java | 33 ++++++++++++++++++- .../jdbc/OpenTelemetryJulHandlerTest.java | 18 ++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java index 97408d3de471..a41c5cf6d32e 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java @@ -16,7 +16,9 @@ package com.google.cloud.bigquery.jdbc; +import com.google.cloud.logging.LogEntry; import com.google.cloud.logging.Logging; +import com.google.cloud.logging.Payload; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.baggage.Baggage; import io.opentelemetry.api.common.AttributeKey; @@ -26,6 +28,7 @@ import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.context.Context; import java.time.Instant; +import java.util.Collections; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; @@ -84,7 +87,35 @@ public void publish(LogRecord record) { } private void publishToGcp(LogRecord record, String traceId, String spanId, String connectionId) { - // TODO(b/491238299) + if (loggingClient == null) { + return; + } + + LogEntry.Builder builder = + LogEntry.newBuilder(Payload.StringPayload.of(record.getMessage())) + .setSeverity(mapGcpSeverity(record.getLevel())) + .setTimestamp(record.getMillis()); + + if (traceId != null) { + builder.setTrace(traceId); + } + if (spanId != null) { + builder.setSpanId(spanId); + } + if (connectionId != null) { + builder.addLabel("jdbc.connection_id", connectionId); + } + + loggingClient.write(Collections.singleton(builder.build())); + } + + private com.google.cloud.logging.Severity mapGcpSeverity(Level level) { + if (level == Level.SEVERE) return com.google.cloud.logging.Severity.ERROR; + if (level == Level.WARNING) return com.google.cloud.logging.Severity.WARNING; + if (level == Level.INFO) return com.google.cloud.logging.Severity.INFO; + if (level == Level.CONFIG) return com.google.cloud.logging.Severity.INFO; + if (level == Level.FINE) return com.google.cloud.logging.Severity.DEBUG; + return com.google.cloud.logging.Severity.DEBUG; } private void publishToOTel(LogRecord record, String traceId, String spanId, String connectionId) { diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java index 246d7cde0735..c6080ce3607c 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java @@ -18,7 +18,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import com.google.cloud.logging.Logging; import io.opentelemetry.api.baggage.Baggage; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.logs.Severity; @@ -89,4 +92,19 @@ public void testPublishToOTelWithBaggage() { "test-uuid", log.getAttributes().get(AttributeKey.stringKey("jdbc.connection_id"))); } } + + @Test + public void testPublishToGcp() { + Logging loggingClient = mock(Logging.class); + OpenTelemetryJulHandler handler = + new OpenTelemetryJulHandler(loggingClient, otelTesting.getOpenTelemetry(), true, null); + + LogRecord record = new LogRecord(Level.INFO, "Test message"); + record.setLoggerName("test.logger"); + record.setMillis(System.currentTimeMillis()); + + handler.publish(record); + + verify(loggingClient).write(org.mockito.ArgumentMatchers.any(Iterable.class)); + } } From 93b15fb056c79346ecb1f52502dd5a297e956c6b Mon Sep 17 00:00:00 2001 From: Keshav Dandeva Date: Tue, 5 May 2026 14:15:39 +0000 Subject: [PATCH 03/11] chore: update test and add flush method --- .../cloud/bigquery/jdbc/OpenTelemetryJulHandler.java | 7 +++++-- .../bigquery/jdbc/OpenTelemetryJulHandlerTest.java | 12 +++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java index a41c5cf6d32e..f0af7ee854df 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java @@ -87,6 +87,7 @@ public void publish(LogRecord record) { } private void publishToGcp(LogRecord record, String traceId, String spanId, String connectionId) { + // TODO(b/491238299): May require refinement for structured logging or error handling if (loggingClient == null) { return; } @@ -153,11 +154,13 @@ private Severity mapSeverity(Level level) { @Override public void flush() { - // TODO(b/491238299) - needed for publishToGcp + if (isGcpFallback && loggingClient != null) { + loggingClient.flush(); + } } @Override public void close() throws SecurityException { - // TODO(b/491238299) - needed for publishToGcp + // TODO(b/491238299): Implement with gcp exporter logic } } diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java index c6080ce3607c..a1d4e9809da7 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java @@ -21,7 +21,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import com.google.cloud.logging.LogEntry; import com.google.cloud.logging.Logging; +import com.google.cloud.logging.Payload; import io.opentelemetry.api.baggage.Baggage; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.logs.Severity; @@ -33,6 +35,7 @@ import java.util.logging.LogRecord; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import org.mockito.ArgumentCaptor; public class OpenTelemetryJulHandlerTest { @@ -105,6 +108,13 @@ public void testPublishToGcp() { handler.publish(record); - verify(loggingClient).write(org.mockito.ArgumentMatchers.any(Iterable.class)); + ArgumentCaptor> captor = ArgumentCaptor.forClass(Iterable.class); + verify(loggingClient).write(captor.capture()); + + Iterable entries = captor.getValue(); + LogEntry entry = entries.iterator().next(); + + assertEquals("Test message", ((Payload.StringPayload) entry.getPayload()).getData()); + assertEquals(com.google.cloud.logging.Severity.INFO, entry.getSeverity()); } } From 6f3ac5a19700f3688b903ce24971097f4ea32cba Mon Sep 17 00:00:00 2001 From: Keshav Dandeva Date: Wed, 6 May 2026 00:36:38 +0000 Subject: [PATCH 04/11] chore: use global handler --- .../bigquery/jdbc/BigQueryConnection.java | 8 +- .../jdbc/BigQueryJdbcOpenTelemetry.java | 45 ++++++++++ .../jdbc/OpenTelemetryJulHandler.java | 87 ++++++++++--------- .../jdbc/OpenTelemetryJulHandlerTest.java | 81 ++++++++--------- 4 files changed, 128 insertions(+), 93 deletions(-) diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnection.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnection.java index d7fd3d8d19d8..97c01edc2e3e 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnection.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryConnection.java @@ -64,7 +64,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; -import java.util.logging.Logger; /** * An implementation of {@link java.sql.Connection} for establishing a connection with BigQuery and @@ -955,6 +954,7 @@ private void closeImpl() throws SQLException { } finally { BigQueryJdbcMdc.removeInstance(this); BigQueryJdbcRootLogger.closeConnectionHandler(this.connectionId); + BigQueryJdbcOpenTelemetry.unregisterConnection(this.connectionId); } this.isClosed = true; } @@ -1059,10 +1059,8 @@ private BigQuery getBigQueryConnection() { this.enableGcpTraceExporter, this.enableGcpLogExporter, this.customOpenTelemetry); if (this.enableGcpLogExporter || this.customOpenTelemetry != null) { - OpenTelemetryJulHandler otelHandler = - new OpenTelemetryJulHandler( - null, openTelemetry, this.enableGcpLogExporter, this.connectionId); - Logger.getLogger("com.google.cloud.bigquery").addHandler(otelHandler); + BigQueryJdbcOpenTelemetry.registerConnection( + this.connectionId, openTelemetry, null, this.enableGcpLogExporter); } if (this.enableGcpTraceExporter || this.customOpenTelemetry != null) { diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOpenTelemetry.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOpenTelemetry.java index 181e15629c5b..f07b373147ed 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOpenTelemetry.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOpenTelemetry.java @@ -16,15 +16,60 @@ package com.google.cloud.bigquery.jdbc; +import com.google.cloud.logging.Logging; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.trace.Tracer; +import java.util.Collection; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; public class BigQueryJdbcOpenTelemetry { static final String INSTRUMENTATION_SCOPE_NAME = "com.google.cloud.bigquery.jdbc"; + static final String BIGQUERY_NAMESPACE = "com.google.cloud.bigquery"; + + static class TelemetryConfig { + final OpenTelemetry openTelemetry; + final Logging loggingClient; + final boolean isGcpFallback; + + TelemetryConfig(OpenTelemetry openTelemetry, Logging loggingClient, boolean isGcpFallback) { + this.openTelemetry = openTelemetry; + this.loggingClient = loggingClient; + this.isGcpFallback = isGcpFallback; + } + } + + private static final ConcurrentHashMap connectionConfigs = + new ConcurrentHashMap<>(); private BigQueryJdbcOpenTelemetry() {} + static { + Logger.getLogger(BIGQUERY_NAMESPACE).addHandler(new OpenTelemetryJulHandler()); + } + + public static void registerConnection( + String connectionId, + OpenTelemetry openTelemetry, + Logging loggingClient, + boolean isGcpFallback) { + connectionConfigs.put( + connectionId, new TelemetryConfig(openTelemetry, loggingClient, isGcpFallback)); + } + + public static void unregisterConnection(String connectionId) { + connectionConfigs.remove(connectionId); + } + + public static TelemetryConfig getConnectionConfig(String connectionId) { + return connectionConfigs.get(connectionId); + } + + public static Collection getRegisteredConfigs() { + return connectionConfigs.values(); + } + /** * Initializes or returns the OpenTelemetry instance based on hybrid logic. Prefer * customOpenTelemetry if provided; fallback to an auto-configured GCP exporter if requested. diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java index f0af7ee854df..40edfac8a2ce 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java @@ -22,6 +22,7 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.baggage.Baggage; import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.logs.LogRecordBuilder; import io.opentelemetry.api.logs.Logger; import io.opentelemetry.api.logs.Severity; import io.opentelemetry.api.trace.Span; @@ -39,21 +40,7 @@ */ public class OpenTelemetryJulHandler extends Handler { - private final Logging loggingClient; - private final OpenTelemetry openTelemetry; - private final boolean isGcpFallback; - private final String expectedConnectionId; - - public OpenTelemetryJulHandler( - Logging loggingClient, - OpenTelemetry openTelemetry, - boolean isGcpFallback, - String expectedConnectionId) { - this.loggingClient = loggingClient; - this.openTelemetry = openTelemetry; - this.isGcpFallback = isGcpFallback; - this.expectedConnectionId = expectedConnectionId; - } + public OpenTelemetryJulHandler() {} @Override public void publish(LogRecord record) { @@ -61,32 +48,38 @@ public void publish(LogRecord record) { return; } - Context context = Context.current(); - SpanContext spanContext = Span.fromContext(context).getSpanContext(); - - String traceId = spanContext.isValid() ? spanContext.getTraceId() : null; - String spanId = spanContext.isValid() ? spanContext.getSpanId() : null; - // Extract connection ID from baggage - String connectionId = Baggage.fromContext(context).getEntryValue("jdbc.connection_id"); + String connectionId = + Baggage.fromContext(Context.current()).getEntryValue("jdbc.connection_id"); // Fallback to MDC if not in baggage (if MDC is available and used) if (connectionId == null) { connectionId = BigQueryJdbcMdc.getConnectionId(); } - if (expectedConnectionId != null && !expectedConnectionId.equals(connectionId)) { + if (connectionId == null) { + return; + } + + BigQueryJdbcOpenTelemetry.TelemetryConfig config = + BigQueryJdbcOpenTelemetry.getConnectionConfig(connectionId); + if (config == null) { return; } - if (isGcpFallback && loggingClient != null) { - publishToGcp(record, traceId, spanId, connectionId); - } else if (openTelemetry != null) { - publishToOTel(record, traceId, spanId, connectionId); + if (config.isGcpFallback && config.loggingClient != null) { + publishToGcp(record, connectionId, config.loggingClient); + } else if (config.openTelemetry != null) { + publishToOTel(record, connectionId, config.openTelemetry); } } - private void publishToGcp(LogRecord record, String traceId, String spanId, String connectionId) { + private void publishToGcp(LogRecord record, String connectionId, Logging loggingClient) { + Context context = Context.current(); + SpanContext spanContext = Span.fromContext(context).getSpanContext(); + String traceId = spanContext.isValid() ? spanContext.getTraceId() : null; + String spanId = spanContext.isValid() ? spanContext.getSpanId() : null; + // TODO(b/491238299): May require refinement for structured logging or error handling if (loggingClient == null) { return; @@ -119,7 +112,7 @@ private com.google.cloud.logging.Severity mapGcpSeverity(Level level) { return com.google.cloud.logging.Severity.DEBUG; } - private void publishToOTel(LogRecord record, String traceId, String spanId, String connectionId) { + private void publishToOTel(LogRecord record, String connectionId, OpenTelemetry openTelemetry) { if (openTelemetry == null) { return; } @@ -128,17 +121,24 @@ private void publishToOTel(LogRecord record, String traceId, String spanId, Stri Logger logger = openTelemetry .getLogsBridge() - .get(loggerName != null ? loggerName : "com.google.cloud.bigquery.jdbc"); - - logger - .logRecordBuilder() - .setBody(record.getMessage()) - .setSeverity(mapSeverity(record.getLevel())) - .setTimestamp(Instant.ofEpochMilli(record.getMillis())) - .setContext(Context.current()) - .setAttribute( - AttributeKey.stringKey("jdbc.connection_id"), connectionId != null ? connectionId : "") - .emit(); + .get( + loggerName != null + ? loggerName + : BigQueryJdbcOpenTelemetry.INSTRUMENTATION_SCOPE_NAME); + + LogRecordBuilder builder = + logger + .logRecordBuilder() + .setBody(record.getMessage()) + .setSeverity(mapSeverity(record.getLevel())) + .setTimestamp(Instant.ofEpochMilli(record.getMillis())) + .setContext(Context.current()); + + if (connectionId != null) { + builder.setAttribute(AttributeKey.stringKey("jdbc.connection_id"), connectionId); + } + + builder.emit(); } private Severity mapSeverity(Level level) { @@ -154,8 +154,11 @@ private Severity mapSeverity(Level level) { @Override public void flush() { - if (isGcpFallback && loggingClient != null) { - loggingClient.flush(); + for (BigQueryJdbcOpenTelemetry.TelemetryConfig config : + BigQueryJdbcOpenTelemetry.getRegisteredConfigs()) { + if (config.isGcpFallback && config.loggingClient != null) { + config.loggingClient.flush(); + } } } diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java index a1d4e9809da7..81a43c6d2905 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java @@ -31,8 +31,8 @@ import io.opentelemetry.sdk.logs.data.LogRecordData; import io.opentelemetry.sdk.testing.junit5.OpenTelemetryExtension; import java.util.List; -import java.util.logging.Level; -import java.util.logging.LogRecord; +import java.util.logging.Logger; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.ArgumentCaptor; @@ -42,71 +42,60 @@ public class OpenTelemetryJulHandlerTest { @RegisterExtension static final OpenTelemetryExtension otelTesting = OpenTelemetryExtension.create(); + private static final Logger logger = Logger.getLogger("com.google.cloud.bigquery"); + + @AfterEach + public void tearDown() { + BigQueryJdbcOpenTelemetry.unregisterConnection("test-uuid"); + BigQueryJdbcOpenTelemetry.unregisterConnection("wrong-uuid"); + BigQueryJdbcOpenTelemetry.unregisterConnection("gcp-uuid"); + } + @Test public void testPublishToOTel() { - OpenTelemetryJulHandler handler = - new OpenTelemetryJulHandler(null, otelTesting.getOpenTelemetry(), false, null); + BigQueryJdbcOpenTelemetry.registerConnection( + "test-uuid", otelTesting.getOpenTelemetry(), null, false); - LogRecord record = new LogRecord(Level.INFO, "Test message"); - record.setLoggerName("test.logger"); - record.setMillis(System.currentTimeMillis()); - - handler.publish(record); + Baggage baggage = Baggage.builder().put("jdbc.connection_id", "test-uuid").build(); + try (Scope scope = baggage.makeCurrent()) { + logger.info("Test message"); + } List logs = otelTesting.getLogRecords(); assertEquals(1, logs.size()); LogRecordData log = logs.get(0); assertEquals("Test message", log.getBody().asString()); - assertEquals("test.logger", log.getInstrumentationScopeInfo().getName()); assertEquals(Severity.INFO, log.getSeverity()); + assertEquals( + "test-uuid", log.getAttributes().get(AttributeKey.stringKey("jdbc.connection_id"))); } @Test - public void testPublishToOTelWithFiltering() { - // Handler expects "correct-uuid" - OpenTelemetryJulHandler handler = - new OpenTelemetryJulHandler(null, otelTesting.getOpenTelemetry(), false, "correct-uuid"); - - // Simulate log with NO connection ID - LogRecord record = new LogRecord(Level.INFO, "Test message"); - record.setLoggerName("test.logger"); - - handler.publish(record); - - List logs = otelTesting.getLogRecords(); - assertTrue(logs.isEmpty()); // Should be filtered out - } - - @Test - public void testPublishToOTelWithBaggage() { - OpenTelemetryJulHandler handler = - new OpenTelemetryJulHandler(null, otelTesting.getOpenTelemetry(), false, null); + public void testPublishWithFiltering() { + // Register for "test-uuid" + BigQueryJdbcOpenTelemetry.registerConnection( + "test-uuid", otelTesting.getOpenTelemetry(), null, false); - Baggage baggage = Baggage.builder().put("jdbc.connection_id", "test-uuid").build(); + // Log with WRONG connection ID + Baggage baggage = Baggage.builder().put("jdbc.connection_id", "wrong-uuid").build(); try (Scope scope = baggage.makeCurrent()) { - LogRecord record = new LogRecord(Level.INFO, "Test message"); - record.setLoggerName("test.logger"); - handler.publish(record); - - List logs = otelTesting.getLogRecords(); - assertEquals(1, logs.size()); - LogRecordData log = logs.get(0); - assertEquals( - "test-uuid", log.getAttributes().get(AttributeKey.stringKey("jdbc.connection_id"))); + logger.info("Test message"); } + + List logs = otelTesting.getLogRecords(); + assertTrue(logs.isEmpty()); // Should be filtered out because "wrong-uuid" has no config } @Test public void testPublishToGcp() { Logging loggingClient = mock(Logging.class); - OpenTelemetryJulHandler handler = - new OpenTelemetryJulHandler(loggingClient, otelTesting.getOpenTelemetry(), true, null); - - LogRecord record = new LogRecord(Level.INFO, "Test message"); - record.setLoggerName("test.logger"); - record.setMillis(System.currentTimeMillis()); + BigQueryJdbcOpenTelemetry.registerConnection( + "gcp-uuid", otelTesting.getOpenTelemetry(), loggingClient, true); - handler.publish(record); + Baggage baggage = Baggage.builder().put("jdbc.connection_id", "gcp-uuid").build(); + try (Scope scope = baggage.makeCurrent()) { + logger.info("Test message"); + } ArgumentCaptor> captor = ArgumentCaptor.forClass(Iterable.class); verify(loggingClient).write(captor.capture()); From e0ccb5722a28f4152878372e87198c0cc311e634 Mon Sep 17 00:00:00 2001 From: Keshav Dandeva Date: Wed, 6 May 2026 00:59:52 +0000 Subject: [PATCH 05/11] chore: use formatted logs --- .../bigquery/jdbc/OpenTelemetryJulHandler.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java index 40edfac8a2ce..d63027a12020 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java @@ -86,7 +86,7 @@ private void publishToGcp(LogRecord record, String connectionId, Logging logging } LogEntry.Builder builder = - LogEntry.newBuilder(Payload.StringPayload.of(record.getMessage())) + LogEntry.newBuilder(Payload.StringPayload.of(formatMessage(record))) .setSeverity(mapGcpSeverity(record.getLevel())) .setTimestamp(record.getMillis()); @@ -129,7 +129,7 @@ private void publishToOTel(LogRecord record, String connectionId, OpenTelemetry LogRecordBuilder builder = logger .logRecordBuilder() - .setBody(record.getMessage()) + .setBody(formatMessage(record)) .setSeverity(mapSeverity(record.getLevel())) .setTimestamp(Instant.ofEpochMilli(record.getMillis())) .setContext(Context.current()); @@ -152,6 +152,19 @@ private Severity mapSeverity(Level level) { return Severity.TRACE; } + private String formatMessage(LogRecord record) { + String message = record.getMessage(); + Object[] params = record.getParameters(); + if (params != null && params.length > 0) { + try { + return java.text.MessageFormat.format(message, params); + } catch (IllegalArgumentException e) { + return message; + } + } + return message; + } + @Override public void flush() { for (BigQueryJdbcOpenTelemetry.TelemetryConfig config : From 9655146975d49f72f2529272ad9b240df823d8ff Mon Sep 17 00:00:00 2001 From: Keshav Dandeva Date: Wed, 6 May 2026 01:12:09 +0000 Subject: [PATCH 06/11] fix: java 8 tests --- .../bigquery/jdbc/OpenTelemetryJulHandlerTest.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java index 81a43c6d2905..cef38f49df97 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java @@ -56,8 +56,11 @@ public void testPublishToOTel() { BigQueryJdbcOpenTelemetry.registerConnection( "test-uuid", otelTesting.getOpenTelemetry(), null, false); + BigQueryConnection mockConnection = mock(BigQueryConnection.class); Baggage baggage = Baggage.builder().put("jdbc.connection_id", "test-uuid").build(); - try (Scope scope = baggage.makeCurrent()) { + try (Scope scope = baggage.makeCurrent(); + BigQueryJdbcMdc.MdcCloseable mdcScope = + BigQueryJdbcMdc.registerInstance(mockConnection, "test-uuid")) { logger.info("Test message"); } @@ -92,8 +95,11 @@ public void testPublishToGcp() { BigQueryJdbcOpenTelemetry.registerConnection( "gcp-uuid", otelTesting.getOpenTelemetry(), loggingClient, true); + BigQueryConnection mockConnection = mock(BigQueryConnection.class); Baggage baggage = Baggage.builder().put("jdbc.connection_id", "gcp-uuid").build(); - try (Scope scope = baggage.makeCurrent()) { + try (Scope scope = baggage.makeCurrent(); + BigQueryJdbcMdc.MdcCloseable mdcScope = + BigQueryJdbcMdc.registerInstance(mockConnection, "gcp-uuid")) { logger.info("Test message"); } From 996b98750c0cead2c69c9489fb6c387d315b8886 Mon Sep 17 00:00:00 2001 From: Keshav Dandeva Date: Wed, 6 May 2026 01:41:34 +0000 Subject: [PATCH 07/11] chore: address pr comments --- .../jdbc/BigQueryJdbcOpenTelemetry.java | 24 ++++++++++++++----- .../jdbc/OpenTelemetryJulHandler.java | 10 +++++--- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOpenTelemetry.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOpenTelemetry.java index f07b373147ed..3bfa2b294d64 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOpenTelemetry.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOpenTelemetry.java @@ -21,6 +21,7 @@ import io.opentelemetry.api.trace.Tracer; import java.util.Collection; import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Handler; import java.util.logging.Logger; public class BigQueryJdbcOpenTelemetry { @@ -31,12 +32,13 @@ public class BigQueryJdbcOpenTelemetry { static class TelemetryConfig { final OpenTelemetry openTelemetry; final Logging loggingClient; - final boolean isGcpFallback; + final boolean useDirectGcpLogging; - TelemetryConfig(OpenTelemetry openTelemetry, Logging loggingClient, boolean isGcpFallback) { + TelemetryConfig( + OpenTelemetry openTelemetry, Logging loggingClient, boolean useDirectGcpLogging) { this.openTelemetry = openTelemetry; this.loggingClient = loggingClient; - this.isGcpFallback = isGcpFallback; + this.useDirectGcpLogging = useDirectGcpLogging; } } @@ -46,16 +48,26 @@ static class TelemetryConfig { private BigQueryJdbcOpenTelemetry() {} static { - Logger.getLogger(BIGQUERY_NAMESPACE).addHandler(new OpenTelemetryJulHandler()); + Logger logger = Logger.getLogger(BIGQUERY_NAMESPACE); + boolean present = false; + for (Handler h : logger.getHandlers()) { + if (h instanceof OpenTelemetryJulHandler) { + present = true; + break; + } + } + if (!present) { + logger.addHandler(new OpenTelemetryJulHandler()); + } } public static void registerConnection( String connectionId, OpenTelemetry openTelemetry, Logging loggingClient, - boolean isGcpFallback) { + boolean useDirectGcpLogging) { connectionConfigs.put( - connectionId, new TelemetryConfig(openTelemetry, loggingClient, isGcpFallback)); + connectionId, new TelemetryConfig(openTelemetry, loggingClient, useDirectGcpLogging)); } public static void unregisterConnection(String connectionId) { diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java index d63027a12020..905c044ee79d 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java @@ -67,7 +67,7 @@ public void publish(LogRecord record) { return; } - if (config.isGcpFallback && config.loggingClient != null) { + if (config.useDirectGcpLogging && config.loggingClient != null) { publishToGcp(record, connectionId, config.loggingClient); } else if (config.openTelemetry != null) { publishToOTel(record, connectionId, config.openTelemetry); @@ -169,8 +169,12 @@ private String formatMessage(LogRecord record) { public void flush() { for (BigQueryJdbcOpenTelemetry.TelemetryConfig config : BigQueryJdbcOpenTelemetry.getRegisteredConfigs()) { - if (config.isGcpFallback && config.loggingClient != null) { - config.loggingClient.flush(); + if (config.useDirectGcpLogging && config.loggingClient != null) { + try { + config.loggingClient.flush(); + } catch (Exception e) { + // Ignore failures during flush to protect other connections + } } } } From 6481bff718b426e72d513ed87aca0f90b56150e2 Mon Sep 17 00:00:00 2001 From: Keshav Dandeva Date: Wed, 6 May 2026 01:45:26 +0000 Subject: [PATCH 08/11] fix: split unit tests --- .../jdbc/OpenTelemetryJulHandler.java | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java index 905c044ee79d..5d1e1b79ac18 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java @@ -48,29 +48,33 @@ public void publish(LogRecord record) { return; } - // Extract connection ID from baggage - String connectionId = - Baggage.fromContext(Context.current()).getEntryValue("jdbc.connection_id"); - - // Fallback to MDC if not in baggage (if MDC is available and used) - if (connectionId == null) { - connectionId = BigQueryJdbcMdc.getConnectionId(); - } + try { + // Extract connection ID from baggage + String connectionId = + Baggage.fromContext(Context.current()).getEntryValue("jdbc.connection_id"); + + // Fallback to MDC if not in baggage (if MDC is available and used) + if (connectionId == null) { + connectionId = BigQueryJdbcMdc.getConnectionId(); + } - if (connectionId == null) { - return; - } + if (connectionId == null) { + return; + } - BigQueryJdbcOpenTelemetry.TelemetryConfig config = - BigQueryJdbcOpenTelemetry.getConnectionConfig(connectionId); - if (config == null) { - return; - } + BigQueryJdbcOpenTelemetry.TelemetryConfig config = + BigQueryJdbcOpenTelemetry.getConnectionConfig(connectionId); + if (config == null) { + return; + } - if (config.useDirectGcpLogging && config.loggingClient != null) { - publishToGcp(record, connectionId, config.loggingClient); - } else if (config.openTelemetry != null) { - publishToOTel(record, connectionId, config.openTelemetry); + if (config.useDirectGcpLogging && config.loggingClient != null) { + publishToGcp(record, connectionId, config.loggingClient); + } else if (config.openTelemetry != null) { + publishToOTel(record, connectionId, config.openTelemetry); + } + } catch (Throwable t) { + // Ignore exceptions to prevent breaking application logging or other handlers } } From 315f9ad4c1dc58dafef8884b91aa931cb260da86 Mon Sep 17 00:00:00 2001 From: Keshav Dandeva Date: Wed, 6 May 2026 01:59:36 +0000 Subject: [PATCH 09/11] fix tests --- .../cloud/bigquery/jdbc/BigQueryJdbcLoggingBaseTest.java | 1 + .../cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcLoggingBaseTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcLoggingBaseTest.java index b97a7bd1e0f4..2196363663fb 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcLoggingBaseTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcLoggingBaseTest.java @@ -34,6 +34,7 @@ public abstract class BigQueryJdbcLoggingBaseTest extends BigQueryJdbcBaseTest { @BeforeEach public void setUpLogValidator() { logger = BigQueryJdbcRootLogger.getRootLogger(); + logger.setLevel(java.util.logging.Level.ALL); capturedLogs.clear(); threadId = Thread.currentThread().getId(); handler = diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java index cef38f49df97..657b6240daa0 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.logging.Logger; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.ArgumentCaptor; @@ -44,6 +45,11 @@ public class OpenTelemetryJulHandlerTest { private static final Logger logger = Logger.getLogger("com.google.cloud.bigquery"); + @BeforeEach + public void setUp() { + logger.setLevel(java.util.logging.Level.ALL); + } + @AfterEach public void tearDown() { BigQueryJdbcOpenTelemetry.unregisterConnection("test-uuid"); From 19121c42d37c2e6887529ed7ac8b527ec09da48e Mon Sep 17 00:00:00 2001 From: Keshav Dandeva Date: Wed, 6 May 2026 02:13:21 +0000 Subject: [PATCH 10/11] fix: failing tests --- .../google/cloud/bigquery/jdbc/BigQueryJdbcOpenTelemetry.java | 4 ++++ .../cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java | 1 + 2 files changed, 5 insertions(+) diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOpenTelemetry.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOpenTelemetry.java index 3bfa2b294d64..0bea7cdd1886 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOpenTelemetry.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOpenTelemetry.java @@ -48,6 +48,10 @@ static class TelemetryConfig { private BigQueryJdbcOpenTelemetry() {} static { + ensureGlobalHandlerAttached(); + } + + public static void ensureGlobalHandlerAttached() { Logger logger = Logger.getLogger(BIGQUERY_NAMESPACE); boolean present = false; for (Handler h : logger.getHandlers()) { diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java index 657b6240daa0..fce3ae1a99e7 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java @@ -48,6 +48,7 @@ public class OpenTelemetryJulHandlerTest { @BeforeEach public void setUp() { logger.setLevel(java.util.logging.Level.ALL); + BigQueryJdbcOpenTelemetry.ensureGlobalHandlerAttached(); } @AfterEach From ea35185409bab3d7ba3d24139b30815a25b29552 Mon Sep 17 00:00:00 2001 From: Keshav Dandeva Date: Wed, 6 May 2026 12:56:21 +0000 Subject: [PATCH 11/11] chore: address pr feedback --- .../cloud/bigquery/jdbc/BigQueryDriver.java | 11 ++++------- .../jdbc/BigQueryJdbcOpenTelemetry.java | 1 + .../bigquery/jdbc/BigQueryJdbcRootLogger.java | 3 +-- .../jdbc/OpenTelemetryJulHandler.java | 16 ++++++---------- .../jdbc/OpenTelemetryJulHandlerTest.java | 19 +++++++++++++++---- 5 files changed, 27 insertions(+), 23 deletions(-) diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDriver.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDriver.java index 5312e3eb0d70..1e3146d16a8e 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDriver.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryDriver.java @@ -157,13 +157,10 @@ public Connection connect(String url, Properties info) throws SQLException { if (logPath == null) { logPath = System.getenv(BigQueryJdbcUrlUtility.LOG_PATH_ENV_VAR); } - if (logPath == null) { - // Cloud-Only Mode: Suppress local file creation if GCP log exporter is enabled - if (ds.getEnableGcpLogExporter() && logLevel != Level.OFF) { - logPath = null; - } else { - logPath = BigQueryJdbcUrlUtility.DEFAULT_LOG_PATH; - } + + // Fallback to default path only if not specified and not in Cloud-Only mode + if (logPath == null && !ds.getEnableGcpLogExporter()) { + logPath = BigQueryJdbcUrlUtility.DEFAULT_LOG_PATH; } BigQueryJdbcRootLogger.setLevel(logLevel, logPath); diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOpenTelemetry.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOpenTelemetry.java index 0bea7cdd1886..3216b25d1557 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOpenTelemetry.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJdbcOpenTelemetry.java @@ -28,6 +28,7 @@ public class BigQueryJdbcOpenTelemetry { static final String INSTRUMENTATION_SCOPE_NAME = "com.google.cloud.bigquery.jdbc"; static final String BIGQUERY_NAMESPACE = "com.google.cloud.bigquery"; + public static final String CONNECTION_ID_BAGGAGE_KEY = "jdbc.connection_id"; static class TelemetryConfig { final OpenTelemetry openTelemetry; 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 71d48f0de3dc..f13713fe0004 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 @@ -141,15 +141,14 @@ public static void setLevel(Level level, String logPath) throws IOException { if (logPath != null) { setPath(logPath, level); } - logger.setLevel(level); } else { for (Handler h : logger.getHandlers()) { h.close(); logger.removeHandler(h); } fileHandler = null; - logger.setLevel(Level.OFF); } + logger.setLevel(level); } static void setPath(String logPath, Level level) { diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java index 5d1e1b79ac18..b6be12f972af 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandler.java @@ -51,7 +51,8 @@ public void publish(LogRecord record) { try { // Extract connection ID from baggage String connectionId = - Baggage.fromContext(Context.current()).getEntryValue("jdbc.connection_id"); + Baggage.fromContext(Context.current()) + .getEntryValue(BigQueryJdbcOpenTelemetry.CONNECTION_ID_BAGGAGE_KEY); // Fallback to MDC if not in baggage (if MDC is available and used) if (connectionId == null) { @@ -85,9 +86,6 @@ private void publishToGcp(LogRecord record, String connectionId, Logging logging String spanId = spanContext.isValid() ? spanContext.getSpanId() : null; // TODO(b/491238299): May require refinement for structured logging or error handling - if (loggingClient == null) { - return; - } LogEntry.Builder builder = LogEntry.newBuilder(Payload.StringPayload.of(formatMessage(record))) @@ -101,7 +99,7 @@ private void publishToGcp(LogRecord record, String connectionId, Logging logging builder.setSpanId(spanId); } if (connectionId != null) { - builder.addLabel("jdbc.connection_id", connectionId); + builder.addLabel(BigQueryJdbcOpenTelemetry.CONNECTION_ID_BAGGAGE_KEY, connectionId); } loggingClient.write(Collections.singleton(builder.build())); @@ -117,10 +115,6 @@ private com.google.cloud.logging.Severity mapGcpSeverity(Level level) { } private void publishToOTel(LogRecord record, String connectionId, OpenTelemetry openTelemetry) { - if (openTelemetry == null) { - return; - } - String loggerName = record.getLoggerName(); Logger logger = openTelemetry @@ -139,7 +133,9 @@ private void publishToOTel(LogRecord record, String connectionId, OpenTelemetry .setContext(Context.current()); if (connectionId != null) { - builder.setAttribute(AttributeKey.stringKey("jdbc.connection_id"), connectionId); + builder.setAttribute( + AttributeKey.stringKey(BigQueryJdbcOpenTelemetry.CONNECTION_ID_BAGGAGE_KEY), + connectionId); } builder.emit(); diff --git a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java index fce3ae1a99e7..a404ffeb2c73 100644 --- a/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java +++ b/java-bigquery/google-cloud-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/OpenTelemetryJulHandlerTest.java @@ -64,7 +64,10 @@ public void testPublishToOTel() { "test-uuid", otelTesting.getOpenTelemetry(), null, false); BigQueryConnection mockConnection = mock(BigQueryConnection.class); - Baggage baggage = Baggage.builder().put("jdbc.connection_id", "test-uuid").build(); + Baggage baggage = + Baggage.builder() + .put(BigQueryJdbcOpenTelemetry.CONNECTION_ID_BAGGAGE_KEY, "test-uuid") + .build(); try (Scope scope = baggage.makeCurrent(); BigQueryJdbcMdc.MdcCloseable mdcScope = BigQueryJdbcMdc.registerInstance(mockConnection, "test-uuid")) { @@ -77,7 +80,9 @@ public void testPublishToOTel() { assertEquals("Test message", log.getBody().asString()); assertEquals(Severity.INFO, log.getSeverity()); assertEquals( - "test-uuid", log.getAttributes().get(AttributeKey.stringKey("jdbc.connection_id"))); + "test-uuid", + log.getAttributes() + .get(AttributeKey.stringKey(BigQueryJdbcOpenTelemetry.CONNECTION_ID_BAGGAGE_KEY))); } @Test @@ -87,7 +92,10 @@ public void testPublishWithFiltering() { "test-uuid", otelTesting.getOpenTelemetry(), null, false); // Log with WRONG connection ID - Baggage baggage = Baggage.builder().put("jdbc.connection_id", "wrong-uuid").build(); + Baggage baggage = + Baggage.builder() + .put(BigQueryJdbcOpenTelemetry.CONNECTION_ID_BAGGAGE_KEY, "wrong-uuid") + .build(); try (Scope scope = baggage.makeCurrent()) { logger.info("Test message"); } @@ -103,7 +111,10 @@ public void testPublishToGcp() { "gcp-uuid", otelTesting.getOpenTelemetry(), loggingClient, true); BigQueryConnection mockConnection = mock(BigQueryConnection.class); - Baggage baggage = Baggage.builder().put("jdbc.connection_id", "gcp-uuid").build(); + Baggage baggage = + Baggage.builder() + .put(BigQueryJdbcOpenTelemetry.CONNECTION_ID_BAGGAGE_KEY, "gcp-uuid") + .build(); try (Scope scope = baggage.makeCurrent(); BigQueryJdbcMdc.MdcCloseable mdcScope = BigQueryJdbcMdc.registerInstance(mockConnection, "gcp-uuid")) {