Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2690c44
feat(bigquery-jdbc): configure protobuf plugin and add clientanalytic…
Neenu1995 Jul 2, 2026
c5e4835
Merge branch 'jdbc-telemetry-feature' of github.com:googleapis/google…
Neenu1995 Jul 3, 2026
c1cb7ce
Merge branch 'main' into jdbc-telemetry-feature
Neenu1995 Jul 6, 2026
44598bf
feat(bigquery-jdbc): add telemetry proto schema definition (#13651)
Neenu1995 Jul 6, 2026
a4fa21f
Merge branch 'main' into jdbc-telemetry-feature
Neenu1995 Jul 8, 2026
1ae622f
feat(bigquery-jdbc): add telemetry configuration class (#13710)
Neenu1995 Jul 9, 2026
fd23296
Merge branch 'main' into jdbc-telemetry-feature
Neenu1995 Jul 9, 2026
7f49edd
feat(bigquery-jdbc): implement driver environment builder (#13723)
Neenu1995 Jul 10, 2026
fd9ff3a
Merge branch 'main' into jdbc-telemetry-feature
Neenu1995 Jul 10, 2026
8a79ad8
Merge branch 'main' into jdbc-telemetry-feature
Neenu1995 Jul 13, 2026
5b46669
feat(bigquery-jdbc): implement clearcut transport layer (#13738)
Neenu1995 Jul 14, 2026
92e90b0
Merge branch 'main' into jdbc-telemetry-feature
Neenu1995 Jul 14, 2026
9185b6c
Merge branch 'main' into jdbc-telemetry-feature
Neenu1995 Jul 28, 2026
47605a1
feat(bigquery-jdbc): implement non-blocking telemetry batcher and dis…
Neenu1995 Jul 30, 2026
f9ddada
Merge branch 'main' into jdbc-telemetry-feature
Neenu1995 Aug 7, 2026
dbeb929
Merge branch 'main' into jdbc-telemetry-feature
Neenu1995 Aug 11, 2026
4125735
feat(bigquery-jdbc): add TelemetryManager singleton foundation and ex…
Neenu1995 Aug 11, 2026
6f26033
add gitignore
Neenu1995 Aug 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions java-bigquery-jdbc/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ tools/**/*.class
tools/**/drivers/**
tools/**/logs/**
tools/**/*.jfr
tools/**/odbc/
tools/**/native_odbc_perf

# Gemini/Jetski agent custom skills
.agents/
24 changes: 24 additions & 0 deletions java-bigquery-jdbc/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@
</properties>

<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.1</version>
</extension>
</extensions>
<resources>
<resource>
<directory>src/main/resources</directory>
Expand Down Expand Up @@ -220,6 +227,23 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.25.5:exe:${os.detected.classifier}</protocArtifact>
<checkStaleness>true</checkStaleness>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* 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
*
* https://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.telemetry.v1;

import com.google.api.client.http.ByteArrayContent;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpContent;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.cloud.bigquery.jdbc.BigQueryJdbcCustomLogger;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;

final class ClearcutTransport {
private static final Logger logger =
new BigQueryJdbcCustomLogger(ClearcutTransport.class.getName());
private static final String CONTENT_TYPE_PROTOBUF = "application/x-protobuf";
private static final int DEFAULT_CONNECT_TIMEOUT_MS = 10_000;
private static final int DEFAULT_READ_TIMEOUT_MS = 10_000;

private final HttpTransport httpTransport;
private final TelemetryConfiguration config;
private final HttpRequestFactory requestFactory;

ClearcutTransport(TelemetryConfiguration config) {
this(new NetHttpTransport(), config);
}

// Package-private constructor for testing overrides
ClearcutTransport(HttpTransport httpTransport, TelemetryConfiguration config) {
this.httpTransport = httpTransport;
this.config = config;
this.requestFactory = this.httpTransport.createRequestFactory();
}

TransportResult send(TelemetryPayload payload) {
if (!config.isEnabled()) {
return TransportResult.disabled();
}
if (payload == null) {
logger.log(Level.WARNING, "Cannot send null telemetry payload to Clearcut");
return TransportResult.disabled();
}

long now = System.currentTimeMillis();
LogRequest logRequest =
LogRequest.newBuilder()
.setLogSource(config.getLogSource())
.setRequestTimeMs(now)
.addLogEvents(
LogEvent.newBuilder()
.setEventTimeMs(now)
.setSourceExtension(payload.toByteString())
.build())
.build();

byte[] requestBytes = logRequest.toByteArray();
HttpContent content = new ByteArrayContent(CONTENT_TYPE_PROTOBUF, requestBytes);
GenericUrl url = new GenericUrl(config.getEndpointUrl());

long nextRequestWaitMillis = -1;

try {
HttpRequest request = requestFactory.buildPostRequest(url, content);
request.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT_MS);
request.setReadTimeout(DEFAULT_READ_TIMEOUT_MS);
request.setThrowExceptionOnExecuteError(false);

HttpResponse response = null;
try {
response = request.execute();
int statusCode = response.getStatusCode();

if (response.getContent() != null) {
try (InputStream is = response.getContent()) {
LogResponse logResponse = LogResponse.parseFrom(is);
if (logResponse.getNextRequestWaitMillis() > 0) {
nextRequestWaitMillis = logResponse.getNextRequestWaitMillis();
}
} catch (IOException ignored) {
// Ignore non-protobuf content from error bodies
}
}

boolean success = statusCode >= 200 && statusCode < 300;
if (success) {
logger.log(Level.FINE, "Successfully uploaded telemetry payload to Clearcut");
} else {
logger.log(
Level.WARNING,
String.format("Clearcut upload failed with status code: %d", statusCode));
}
return new TransportResult(success, nextRequestWaitMillis);
} finally {
if (response != null) {
response.disconnect();
}
}
} catch (IOException e) {
logger.log(Level.WARNING, "IOException sending telemetry payload to Clearcut", e);
return new TransportResult(false, nextRequestWaitMillis);
} catch (Throwable t) {
logger.log(Level.WARNING, "Unexpected error sending telemetry payload to Clearcut", t);
return new TransportResult(false, nextRequestWaitMillis);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*
* 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
*
* https://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.telemetry.v1;

import com.google.cloud.bigquery.jdbc.utils.BigQueryJdbcVersionUtility;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;

/** Utility builder for constructing {@link DriverEnvironment} telemetry protos. */
final class DriverEnvironmentBuilder {
private static final Logger logger = Logger.getLogger(DriverEnvironmentBuilder.class.getName());

static final String DRIVER_NAME = "google-bigquery-jdbc-driver";
static final String CLIENT_LANGUAGE = "java";
static final String DEFAULT_TELEMETRY_TAG_DIR = ".bigquery-jdbc";
static final String DEFAULT_TELEMETRY_TAG_FILE = "telemetry-tag";
static final String UNKNOWN = "unknown";
static final String RESTRICTED = "restricted";

private DriverEnvironmentBuilder() {}

static DriverEnvironment build() {
return build(null);
}

static DriverEnvironment build(Path customTelemetryTagPath) {
return DriverEnvironment.newBuilder()
.setDriverName(DRIVER_NAME)
.setDriverVersion(BigQueryJdbcVersionUtility.getSanitizedDriverVersion())
.setClientLanguage(CLIENT_LANGUAGE)
.setClientLanguageVersion(getMajorJavaVersion())
.setOsType(detectOsType())
.setOsVersion(getMajorOsVersion())
.setTelemetryTag(getOrCreateTelemetryTag(customTelemetryTagPath))
.build();
}

static String getMajorJavaVersion() {
try {
return getMajorJavaVersion(System.getProperty("java.version"));
} catch (SecurityException e) {
return RESTRICTED;
}
}

static String getMajorJavaVersion(String versionProperty) {
if (versionProperty == null || versionProperty.trim().isEmpty()) {
return UNKNOWN;
}
String version = versionProperty.trim();
if (version.startsWith("1.")) {
// Legacy Java version format (e.g. 1.8.0_292 -> 8)
String[] parts = version.split("\\.");
if (parts.length >= 2) {
return parts[1];
}
} else {
// Modern Java version format (e.g. 11.0.12 -> 11 or 17.0.1 -> 17)
int firstDot = version.indexOf('.');
if (firstDot > 0) {
return version.substring(0, firstDot);
}
}
return version;
}

static DriverEnvironment.OsType detectOsType() {
try {
return detectOsType(System.getProperty("os.name"));
} catch (SecurityException e) {
return DriverEnvironment.OsType.OS_TYPE_UNSPECIFIED;
}
}

static DriverEnvironment.OsType detectOsType(String osNameProperty) {
if (osNameProperty == null || osNameProperty.trim().isEmpty()) {
return DriverEnvironment.OsType.OS_TYPE_UNKNOWN;
}
String osName = osNameProperty.toLowerCase();
if (osName.contains("mac") || osName.contains("darwin")) {
return DriverEnvironment.OsType.OS_TYPE_MACOS;
} else if (osName.contains("win")) {
return DriverEnvironment.OsType.OS_TYPE_WINDOWS;
} else if (osName.contains("nux") || osName.contains("nix")) {
return DriverEnvironment.OsType.OS_TYPE_LINUX;
} else if (osName.contains("solaris") || osName.contains("sunos")) {
return DriverEnvironment.OsType.OS_TYPE_SOLARIS;
} else if (osName.contains("freebsd")) {
return DriverEnvironment.OsType.OS_TYPE_FREEBSD;
} else if (osName.contains("openbsd")) {
return DriverEnvironment.OsType.OS_TYPE_OPENBSD;
} else if (osName.contains("netbsd")) {
return DriverEnvironment.OsType.OS_TYPE_NETBSD;
} else if (osName.contains("aix")) {
return DriverEnvironment.OsType.OS_TYPE_AIX;
}
return DriverEnvironment.OsType.OS_TYPE_UNKNOWN;
}

static String getMajorOsVersion() {
try {
return getMajorOsVersion(System.getProperty("os.version"));
} catch (SecurityException e) {
return RESTRICTED;
}
}

static String getMajorOsVersion(String osVersionProperty) {
if (osVersionProperty == null || osVersionProperty.trim().isEmpty()) {
return UNKNOWN;
}
String version = osVersionProperty.trim();
int firstDot = version.indexOf('.');
if (firstDot > 0) {
return version.substring(0, firstDot);
}
return version;
}

static String getOrCreateTelemetryTag(Path customFilePath) {
try {
Path idFilePath = customFilePath;
if (idFilePath == null) {
String userHome = System.getProperty("user.home");
if (userHome == null || userHome.trim().isEmpty()) {
return UUID.randomUUID().toString();
}
idFilePath = Paths.get(userHome, DEFAULT_TELEMETRY_TAG_DIR, DEFAULT_TELEMETRY_TAG_FILE);
}

if (Files.exists(idFilePath)) {
try {
String content =
new String(Files.readAllBytes(idFilePath), StandardCharsets.UTF_8).trim();
// Validate existing content is a valid UUID
UUID.fromString(content);
return content;
} catch (Exception e) {
logger.log(
Level.WARNING, "Failed to read or parse telemetry tag from file, regenerating", e);
}
}

String newId = UUID.randomUUID().toString();
try {
if (idFilePath.getParent() != null) {
Files.createDirectories(idFilePath.getParent());
}
Files.write(idFilePath, newId.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
logger.log(Level.WARNING, "Failed to persist telemetry tag to file", e);
}
return newId;
} catch (SecurityException e) {
return UUID.randomUUID().toString();
}
}
}
Loading
Loading