Skip to content

Commit 01734d1

Browse files
committed
feat: add openTelemetry connection properties
1 parent d00ae37 commit 01734d1

4 files changed

Lines changed: 192 additions & 0 deletions

File tree

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
import com.google.cloud.bigquery.storage.v1.BigQueryWriteClient;
4242
import com.google.cloud.bigquery.storage.v1.BigQueryWriteSettings;
4343
import com.google.cloud.http.HttpTransportOptions;
44+
import io.opentelemetry.api.OpenTelemetry;
45+
import io.opentelemetry.api.trace.Tracer;
4446
import java.io.IOException;
4547
import java.io.InputStream;
4648
import java.sql.CallableStatement;
@@ -138,6 +140,9 @@ public class BigQueryConnection extends BigQueryNoOpsConnection {
138140
Long connectionPoolSize;
139141
Long listenerPoolSize;
140142
String partnerToken;
143+
boolean enableOpenTelemetry;
144+
String openTelemetryExporter;
145+
Tracer tracer = OpenTelemetry.noop().getTracer("");
141146

142147
BigQueryConnection(String url) throws IOException {
143148
this(url, DataSource.fromUrl(url));
@@ -242,6 +247,8 @@ public class BigQueryConnection extends BigQueryNoOpsConnection {
242247
this.connectionPoolSize = ds.getConnectionPoolSize();
243248
this.listenerPoolSize = ds.getListenerPoolSize();
244249
this.partnerToken = ds.getPartnerToken();
250+
this.enableOpenTelemetry = ds.getEnableOpenTelemetry();
251+
this.openTelemetryExporter = ds.getOpenTelemetryExporter();
245252

246253
this.headerProvider = createHeaderProvider();
247254
this.bigQuery = getBigQueryConnection();
@@ -935,6 +942,14 @@ private BigQuery getBigQueryConnection() {
935942
bigQueryOptions.setTransportOptions(this.httpTransportOptions);
936943
}
937944

945+
OpenTelemetry openTelemetry =
946+
BigQueryJdbcOpenTelemetry.getOpenTelemetry(this.enableOpenTelemetry, this.openTelemetryExporter);
947+
if (this.enableOpenTelemetry) {
948+
this.tracer = BigQueryJdbcOpenTelemetry.getTracer(openTelemetry);
949+
bigQueryOptions.setOpenTelemetryTracer(this.tracer);
950+
BigQueryJdbcOpenTelemetry.attachLoggingBridge();
951+
}
952+
938953
BigQueryOptions options = bigQueryOptions.setHeaderProvider(this.headerProvider).build();
939954
options.setDefaultJobCreationMode(
940955
this.useStatelessQueryMode
@@ -1083,4 +1098,8 @@ public CallableStatement prepareCall(
10831098
}
10841099
return prepareCall(sql);
10851100
}
1101+
1102+
public Tracer getTracer() {
1103+
return this.tracer;
1104+
}
10861105
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.bigquery.jdbc;
18+
19+
import io.opentelemetry.api.GlobalOpenTelemetry;
20+
import io.opentelemetry.api.OpenTelemetry;
21+
import io.opentelemetry.api.trace.Tracer;
22+
import io.opentelemetry.sdk.OpenTelemetrySdk;
23+
import io.opentelemetry.sdk.trace.SdkTracerProvider;
24+
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
25+
import io.opentelemetry.sdk.trace.export.SpanExporter;
26+
import com.google.cloud.opentelemetry.trace.TraceConfiguration;
27+
import com.google.cloud.opentelemetry.trace.TraceExporter;
28+
import java.io.IOException;
29+
30+
public class BigQueryJdbcOpenTelemetry {
31+
32+
private static final BigQueryJdbcCustomLogger LOG = new BigQueryJdbcCustomLogger(BigQueryJdbcOpenTelemetry.class.getName());
33+
private static final Object lock = new Object();
34+
private static volatile OpenTelemetrySdk autoConfiguredOpenTelemetry;
35+
private static volatile boolean initialized = false;
36+
private static final String INSTRUMENTATION_SCOPE_NAME = "com.google.cloud.bigquery.jdbc";
37+
38+
/**
39+
* Initializes or returns the OpenTelemetry instance based on hybrid logic.
40+
* Prefer GlobalOpenTelemetry; fallback to an auto-configured GCP exporter if requested.
41+
*/
42+
public static OpenTelemetry getOpenTelemetry(boolean enableOpenTelemetry, String exporterType) {
43+
if (!enableOpenTelemetry) {
44+
return OpenTelemetry.noop();
45+
}
46+
47+
OpenTelemetry globalOtel = GlobalOpenTelemetry.get();
48+
if ("gcp".equalsIgnoreCase(exporterType)) {
49+
return getAutoConfiguredOpenTelemetry();
50+
}
51+
52+
return globalOtel;
53+
}
54+
55+
/**
56+
* Gets a Tracer for the JDBC driver instrumentation scope.
57+
*/
58+
public static Tracer getTracer(OpenTelemetry openTelemetry) {
59+
return openTelemetry.getTracer(INSTRUMENTATION_SCOPE_NAME);
60+
}
61+
62+
/**
63+
* TODO(b/491245568): Attaches the OpenTelemetry logging bridge to the root java.util.logging.Logger.
64+
* This is currently a no-op due to shading issues with `opentelemetry-appender-jul`.
65+
*/
66+
public static void attachLoggingBridge() {
67+
// No-op for now.
68+
}
69+
70+
private static OpenTelemetry getAutoConfiguredOpenTelemetry() {
71+
if (!initialized) {
72+
synchronized (lock) {
73+
if (!initialized) {
74+
try {
75+
autoConfiguredOpenTelemetry = initGcpOpenTelemetry();
76+
} catch (Exception e) {
77+
LOG.warning("Failed to initialize OpenTelemetry with GCP exporter: " + e.getMessage());
78+
autoConfiguredOpenTelemetry = null;
79+
} finally {
80+
initialized = true;
81+
}
82+
}
83+
}
84+
}
85+
return autoConfiguredOpenTelemetry != null ? autoConfiguredOpenTelemetry : OpenTelemetry.noop();
86+
}
87+
88+
private static OpenTelemetrySdk initGcpOpenTelemetry() throws IOException {
89+
LOG.info("Initializing BigQuery JDBC standalone OpenTelemetry SDK with GCP exporter.");
90+
91+
SpanExporter traceExporter;
92+
try {
93+
TraceConfiguration configuration = TraceConfiguration.builder().build();
94+
traceExporter = TraceExporter.createWithConfiguration(configuration);
95+
} catch (Exception e) {
96+
throw new RuntimeException("Could not create TraceExporter. Ensure exporter-trace is on classpath.", e);
97+
}
98+
99+
SdkTracerProvider tracerProvider =
100+
SdkTracerProvider.builder()
101+
.addSpanProcessor(BatchSpanProcessor.builder(traceExporter).build())
102+
.build();
103+
104+
OpenTelemetrySdk sdk =
105+
OpenTelemetrySdk.builder()
106+
.setTracerProvider(tracerProvider)
107+
.build();
108+
109+
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
110+
try {
111+
tracerProvider.close();
112+
} catch (Exception e) {
113+
LOG.warning("Error closing tracer provider: " + e.getMessage());
114+
}
115+
}));
116+
117+
return sdk;
118+
}
119+
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ protected boolean removeEldestEntry(Map.Entry<String, Map<String, String>> eldes
162162
static final int DEFAULT_SWA_APPEND_ROW_COUNT_VALUE = 1000;
163163
static final String SWA_ACTIVATION_ROW_COUNT_PROPERTY_NAME = "SWA_ActivationRowCount";
164164
static final int DEFAULT_SWA_ACTIVATION_ROW_COUNT_VALUE = 3;
165+
static final String ENABLE_OPENTELEMETRY_PROPERTY_NAME = "EnableOpenTelemetry";
166+
static final boolean DEFAULT_ENABLE_OPENTELEMETRY_VALUE = false;
167+
static final String OPENTELEMETRY_EXPORTER_PROPERTY_NAME = "OpenTelemetryExporter";
168+
static final String DEFAULT_OPENTELEMETRY_EXPORTER_VALUE = "none";
165169
private static final BigQueryJdbcCustomLogger LOG =
166170
new BigQueryJdbcCustomLogger(BigQueryJdbcUrlUtility.class.getName());
167171
static final String FILTER_TABLES_ON_DEFAULT_DATASET_PROPERTY_NAME =
@@ -607,6 +611,18 @@ protected boolean removeEldestEntry(Map.Entry<String, Map<String, String>> eldes
607611
.setDescription(
608612
"Reason for the request, which is passed as the x-goog-request-reason"
609613
+ " header.")
614+
.build(),
615+
BigQueryConnectionProperty.newBuilder()
616+
.setName(ENABLE_OPENTELEMETRY_PROPERTY_NAME)
617+
.setDescription(
618+
"Enables or disables OpenTelemetry features in the Driver. Disabled by default.")
619+
.setDefaultValue(String.valueOf(DEFAULT_ENABLE_OPENTELEMETRY_VALUE))
620+
.build(),
621+
BigQueryConnectionProperty.newBuilder()
622+
.setName(OPENTELEMETRY_EXPORTER_PROPERTY_NAME)
623+
.setDescription(
624+
"Specifies the auto-configured OpenTelemetry exporter (e.g., gcp).")
625+
.setDefaultValue(DEFAULT_OPENTELEMETRY_EXPORTER_VALUE)
610626
.build())));
611627

612628
private static final List<String> NETWORK_PROPERTIES =

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ public class DataSource implements javax.sql.DataSource {
113113
private String privateServiceConnect;
114114
private Long connectionPoolSize;
115115
private Long listenerPoolSize;
116+
private Boolean enableOpenTelemetry;
117+
private String openTelemetryExporter;
116118

117119
// Make sure the JDBC driver class is loaded.
118120
static {
@@ -324,6 +326,13 @@ public class DataSource implements javax.sql.DataSource {
324326
.put(
325327
BigQueryJdbcUrlUtility.LISTENER_POOL_SIZE_PROPERTY_NAME,
326328
(ds, val) -> ds.setListenerPoolSize(Long.parseLong(val)))
329+
.put(
330+
BigQueryJdbcUrlUtility.ENABLE_OPENTELEMETRY_PROPERTY_NAME,
331+
(ds, val) ->
332+
ds.setEnableOpenTelemetry(
333+
BigQueryJdbcUrlUtility.convertIntToBoolean(
334+
val, BigQueryJdbcUrlUtility.ENABLE_OPENTELEMETRY_PROPERTY_NAME)))
335+
.put(BigQueryJdbcUrlUtility.OPENTELEMETRY_EXPORTER_PROPERTY_NAME, DataSource::setOpenTelemetryExporter)
327336
.build();
328337

329338
public static DataSource fromUrl(String url) {
@@ -616,6 +625,15 @@ private Properties createProperties() {
616625
BigQueryJdbcUrlUtility.LISTENER_POOL_SIZE_PROPERTY_NAME,
617626
String.valueOf(this.listenerPoolSize));
618627
}
628+
if (this.enableOpenTelemetry != null) {
629+
connectionProperties.setProperty(
630+
BigQueryJdbcUrlUtility.ENABLE_OPENTELEMETRY_PROPERTY_NAME,
631+
String.valueOf(this.enableOpenTelemetry));
632+
}
633+
if (this.openTelemetryExporter != null) {
634+
connectionProperties.setProperty(
635+
BigQueryJdbcUrlUtility.OPENTELEMETRY_EXPORTER_PROPERTY_NAME, this.openTelemetryExporter);
636+
}
619637
return connectionProperties;
620638
}
621639

@@ -737,6 +755,26 @@ public void setListenerPoolSize(Long listenerPoolSize) {
737755
this.listenerPoolSize = listenerPoolSize;
738756
}
739757

758+
public Boolean getEnableOpenTelemetry() {
759+
return enableOpenTelemetry != null
760+
? enableOpenTelemetry
761+
: BigQueryJdbcUrlUtility.DEFAULT_ENABLE_OPENTELEMETRY_VALUE;
762+
}
763+
764+
public void setEnableOpenTelemetry(Boolean enableOpenTelemetry) {
765+
this.enableOpenTelemetry = enableOpenTelemetry;
766+
}
767+
768+
public String getOpenTelemetryExporter() {
769+
return openTelemetryExporter != null
770+
? openTelemetryExporter
771+
: BigQueryJdbcUrlUtility.DEFAULT_OPENTELEMETRY_EXPORTER_VALUE;
772+
}
773+
774+
public void setOpenTelemetryExporter(String openTelemetryExporter) {
775+
this.openTelemetryExporter = openTelemetryExporter;
776+
}
777+
740778
public void setHighThroughputMinTableSize(Integer highThroughputMinTableSize) {
741779
this.highThroughputMinTableSize = highThroughputMinTableSize;
742780
}

0 commit comments

Comments
 (0)