From 4cca913e353c80584b4cf82e175fa4cb9677111e Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Tue, 25 Aug 2026 11:32:51 -0400 Subject: [PATCH 1/4] feat(telemetry): implement PR 8 opt-out controls, enum converters and recording engine --- .../jdbc/telemetry/v1/TelemetryBatcher.java | 69 ++++++++++- .../telemetry/v1/TelemetryConfiguration.java | 27 +++++ .../jdbc/telemetry/v1/TelemetryManager.java | 108 +++++++++++++++++- .../jdbc/telemetry/v1/telemetry.proto | 6 +- .../telemetry/v1/TelemetryBatcherTest.java | 2 +- .../telemetry/v1/TelemetryManagerTest.java | 70 ++++++++++++ 6 files changed, 273 insertions(+), 9 deletions(-) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryBatcher.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryBatcher.java index 6b5e08c50fc6..80f1dc4352e4 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryBatcher.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryBatcher.java @@ -21,7 +21,9 @@ import com.google.protobuf.Timestamp; import java.time.Instant; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; @@ -198,17 +200,76 @@ TransportResult flush() { } private void populatePayloadBuilder(TelemetryPayload.Builder builder, List events) { + Map connections = new HashMap<>(); + Map statements = new HashMap<>(); + Map errors = new HashMap<>(); + Map features = new HashMap<>(); + for (Message event : events) { if (event instanceof ConnectionAttempt) { - builder.addConnectionAttempts((ConnectionAttempt) event); + ConnectionAttempt attempt = (ConnectionAttempt) event; + String key = attempt.getAuthType().name() + "|" + attempt.getStatus().name() + "|" + attempt.getErrorCode(); + ConnectionAttempt.Builder b = connections.get(key); + if (b == null) { + connections.put(key, attempt.toBuilder()); + } else { + b.setCount(b.getCount() + attempt.getCount()); + } } else if (event instanceof StatementExecution) { - builder.addStatementExecutions((StatementExecution) event); + StatementExecution exec = (StatementExecution) event; + String key = exec.getStatementType().name() + "|" + exec.getQueryApiType().name() + "|" + exec.getStatus().name() + "|" + exec.getErrorCode(); + StatementExecution.Builder b = statements.get(key); + if (b == null) { + statements.put(key, exec.toBuilder()); + } else { + b.setCount(b.getCount() + exec.getCount()); + if (exec.hasDuration()) { + DurationHistogram.Builder durB = b.getDurationBuilder(); + DurationHistogram dur = exec.getDuration(); + durB.setCount(durB.getCount() + dur.getCount()); + durB.setSum(durB.getSum() + dur.getSum()); + for (int i = 0; i < dur.getBucketCountsCount(); i++) { + if (i < durB.getBucketCountsCount()) { + durB.setBucketCounts(i, durB.getBucketCounts(i) + dur.getBucketCounts(i)); + } else { + durB.addBucketCounts(dur.getBucketCounts(i)); + } + } + } + } } else if (event instanceof ErrorMetric) { - builder.addErrors((ErrorMetric) event); + ErrorMetric err = (ErrorMetric) event; + String key = err.getErrorCode() + "|" + err.getErrorXdbcCode() + "|" + err.getMethodName(); + ErrorMetric.Builder b = errors.get(key); + if (b == null) { + errors.put(key, err.toBuilder()); + } else { + b.setCount(b.getCount() + err.getCount()); + } } else if (event instanceof FeatureUsage) { - builder.addFeatureUsages((FeatureUsage) event); + FeatureUsage feat = (FeatureUsage) event; + String key = feat.getDriverFeature().name() + "|" + feat.getCustomFeatureName(); + FeatureUsage.Builder b = features.get(key); + if (b == null) { + features.put(key, feat.toBuilder()); + } else { + b.setCount(b.getCount() + feat.getCount()); + } } } + + for (ConnectionAttempt.Builder b : connections.values()) { + builder.addConnectionAttempts(b); + } + for (StatementExecution.Builder b : statements.values()) { + builder.addStatementExecutions(b); + } + for (ErrorMetric.Builder b : errors.values()) { + builder.addErrors(b); + } + for (FeatureUsage.Builder b : features.values()) { + builder.addFeatureUsages(b); + } } @Override diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryConfiguration.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryConfiguration.java index 85e10c5fe376..4f57507fbc78 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryConfiguration.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryConfiguration.java @@ -156,6 +156,33 @@ Builder setDriverEnvironment(DriverEnvironment driverEnvironment) { return this; } + Builder resolveEnabledFlag(java.util.Properties connectionProperties) { + if (connectionProperties != null) { + String propValue = connectionProperties.getProperty("EnableDiagnosticTelemetry"); + if (propValue == null) { + propValue = connectionProperties.getProperty("enableDiagnosticTelemetry"); + } + if (propValue != null) { + if ("0".equals(propValue) || "false".equalsIgnoreCase(propValue)) { + this.enabled = false; + return this; + } + if ("1".equals(propValue) || "true".equalsIgnoreCase(propValue)) { + this.enabled = true; + return this; + } + } + } + String envValue = System.getenv("GOOGLE_CLOUD_TELEMETRY_ENABLED"); + if (envValue != null) { + if ("0".equals(envValue) || "false".equalsIgnoreCase(envValue)) { + this.enabled = false; + return this; + } + } + return this; + } + TelemetryConfiguration build() { return new TelemetryConfiguration(this); } diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManager.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManager.java index a688cca15f24..a5e76e312014 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManager.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManager.java @@ -16,6 +16,7 @@ package com.google.cloud.bigquery.jdbc.telemetry.v1; +import com.google.cloud.bigquery.JobStatistics.QueryStatistics; import com.google.cloud.bigquery.jdbc.BigQueryJdbcCustomLogger; import java.util.logging.Level; import java.util.logging.Logger; @@ -44,12 +45,16 @@ private TelemetryManager(TelemetryBatcher batcher) { * and transport. */ static TelemetryManager getInstance() { + return getInstance(null); + } + + static TelemetryManager getInstance(java.util.Properties properties) { TelemetryManager localRef = instance; if (localRef == null) { synchronized (TelemetryManager.class) { localRef = instance; if (localRef == null) { - TelemetryConfiguration config = TelemetryConfiguration.builder().build(); + TelemetryConfiguration config = TelemetryConfiguration.builder().resolveEnabledFlag(properties).build(); ClearcutTransport transport = new ClearcutTransport(config); TelemetryBatcher batcher = new TelemetryBatcher(config, transport); localRef = new TelemetryManager(batcher); @@ -116,4 +121,105 @@ public void close() { batcher.close(); } } + + static StatementType toStatementType(QueryStatistics.StatementType bqStatementType) { + if (bqStatementType == null) { + return StatementType.STATEMENT_TYPE_UNSPECIFIED; + } + switch (bqStatementType.name()) { + case "SELECT": return StatementType.STATEMENT_TYPE_SELECT; + case "INSERT": return StatementType.STATEMENT_TYPE_INSERT; + case "UPDATE": return StatementType.STATEMENT_TYPE_UPDATE; + case "DELETE": return StatementType.STATEMENT_TYPE_DELETE; + case "MERGE": return StatementType.STATEMENT_TYPE_MERGE; + case "CREATE_TABLE": + case "CREATE_TABLE_AS_SELECT": return StatementType.STATEMENT_TYPE_CREATE_TABLE; + case "CREATE_MODEL": return StatementType.STATEMENT_TYPE_CREATE_MODEL; + case "CREATE_VIEW": return StatementType.STATEMENT_TYPE_CREATE_VIEW; + case "DROP_TABLE": return StatementType.STATEMENT_TYPE_DROP_TABLE; + case "DROP_VIEW": return StatementType.STATEMENT_TYPE_DROP_VIEW; + case "ALTER_TABLE": return StatementType.STATEMENT_TYPE_ALTER_TABLE; + case "ALTER_VIEW": return StatementType.STATEMENT_TYPE_ALTER_VIEW; + case "CALL": return StatementType.STATEMENT_TYPE_CALL; + case "SCRIPT": return StatementType.STATEMENT_TYPE_SCRIPT; + default: return StatementType.STATEMENT_TYPE_OTHER; + } + } + + static AuthenticationType toAuthenticationType(int oauthType) { + switch (oauthType) { + case 0: return AuthenticationType.AUTHENTICATION_TYPE_SERVICE_ACCOUNT; + case 1: return AuthenticationType.AUTHENTICATION_TYPE_USER_AUTHENTICATION; + case 2: return AuthenticationType.AUTHENTICATION_TYPE_APPLICATION_DEFAULT_CREDENTIALS; + case 3: return AuthenticationType.AUTHENTICATION_TYPE_EXTERNAL; + case 4: return AuthenticationType.AUTHENTICATION_TYPE_TOKEN; + default: return AuthenticationType.AUTHENTICATION_TYPE_CUSTOM; + } + } + + static final double[] HISTOGRAM_BOUNDS = {10.0, 50.0, 100.0, 250.0, 500.0, 1000.0, 5000.0, 10000.0}; + + static DurationHistogram toDurationBucketMs(long durationMs) { + DurationHistogram.Builder builder = DurationHistogram.newBuilder() + .setCount(1) + .setSum(durationMs); + + int bucketIndex = HISTOGRAM_BOUNDS.length; + for (int i = 0; i < HISTOGRAM_BOUNDS.length; i++) { + builder.addExplicitBounds(HISTOGRAM_BOUNDS[i]); + if (bucketIndex == HISTOGRAM_BOUNDS.length && durationMs < HISTOGRAM_BOUNDS[i]) { + bucketIndex = i; + } + } + for (int i = 0; i <= HISTOGRAM_BOUNDS.length; i++) { + builder.addBucketCounts(i == bucketIndex ? 1L : 0L); + } + return builder.build(); + } + + static void recordConnectionAttempt(Status status, int errorCode, AuthenticationType authType) { + runSafely(() -> { + TelemetryManager mgr = instance; + if (mgr != null && mgr.getBatcher() != null) { + mgr.getBatcher().offerConnectionAttempt( + ConnectionAttempt.newBuilder() + .setStatus(status) + .setErrorCode(errorCode) + .setAuthType(authType) + .setCount(1) + .build()); + } + }); + } + + static void recordStatementExecution(StatementType statementType, QueryApiType apiType, Status status, int errorCode, long durationMs) { + runSafely(() -> { + TelemetryManager mgr = instance; + if (mgr != null && mgr.getBatcher() != null) { + mgr.getBatcher().offerStatementExecution( + StatementExecution.newBuilder() + .setStatementType(statementType) + .setQueryApiType(apiType) + .setStatus(status) + .setErrorCode(errorCode) + .setCount(1) + .setDuration(toDurationBucketMs(durationMs)) + .build()); + } + }); + } + + static void recordFeatureUsage(DriverFeature feature, String customFeatureName) { + runSafely(() -> { + TelemetryManager mgr = instance; + if (mgr != null && mgr.getBatcher() != null) { + mgr.getBatcher().offerFeatureUsage( + FeatureUsage.newBuilder() + .setDriverFeature(feature) + .setCustomFeatureName(customFeatureName == null ? "" : customFeatureName) + .setCount(1) + .build()); + } + }); + } } diff --git a/java-bigquery-jdbc/src/main/proto/google/cloud/bigquery/jdbc/telemetry/v1/telemetry.proto b/java-bigquery-jdbc/src/main/proto/google/cloud/bigquery/jdbc/telemetry/v1/telemetry.proto index b4ac091294cc..b0d1f9a19d01 100644 --- a/java-bigquery-jdbc/src/main/proto/google/cloud/bigquery/jdbc/telemetry/v1/telemetry.proto +++ b/java-bigquery-jdbc/src/main/proto/google/cloud/bigquery/jdbc/telemetry/v1/telemetry.proto @@ -77,7 +77,7 @@ enum AuthenticationType { // Aggregated metrics for connection attempts. message ConnectionAttempt { Status status = 1; - string error_code = 2; + int32 error_code = 2; AuthenticationType auth_type = 3; int64 count = 4; } @@ -117,7 +117,7 @@ message StatementExecution { StatementType statement_type = 1; QueryApiType query_api_type = 2; Status status = 3; - string error_code = 4; + int32 error_code = 4; int64 count = 5; DurationHistogram duration = 6; } @@ -139,7 +139,7 @@ message DurationHistogram { // Aggregated error counts grouped by error code, SQL state, and method. message ErrorMetric { - string error_code = 1; + int32 error_code = 1; string error_xdbc_code = 2; string method_name = 3; int64 count = 4; diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryBatcherTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryBatcherTest.java index d3a5d97b8bf5..bf63749705eb 100644 --- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryBatcherTest.java +++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryBatcherTest.java @@ -87,7 +87,7 @@ public LowLevelHttpResponse execute() { batcher.offerStatementExecution( StatementExecution.newBuilder().setStatus(Status.STATUS_SUCCESS).build()); batcher.offerErrorMetric( - ErrorMetric.newBuilder().setErrorCode("ERR_001").setCount(1).build()); + ErrorMetric.newBuilder().setErrorCode(1).setCount(1).build()); batcher.offerFeatureUsage( FeatureUsage.newBuilder().setDriverFeature(DriverFeature.DRIVER_FEATURE_CUSTOM).build()); diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManagerTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManagerTest.java index 0c71941cd5dc..dc26ef68a6a7 100644 --- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManagerTest.java +++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManagerTest.java @@ -16,11 +16,14 @@ package com.google.cloud.bigquery.jdbc.telemetry.v1; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; +import com.google.cloud.bigquery.JobStatistics.QueryStatistics; +import java.util.Properties; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -102,4 +105,71 @@ public void testRunSafely_handlesNullActionGracefully() { TelemetryManager.runSafely(null); // Verifies no NullPointerException is thrown } + + @Test + public void testToStatementType() { + assertEquals(StatementType.STATEMENT_TYPE_SELECT, TelemetryManager.toStatementType(QueryStatistics.StatementType.SELECT)); + assertEquals(StatementType.STATEMENT_TYPE_INSERT, TelemetryManager.toStatementType(QueryStatistics.StatementType.INSERT)); + assertEquals(StatementType.STATEMENT_TYPE_UPDATE, TelemetryManager.toStatementType(QueryStatistics.StatementType.UPDATE)); + assertEquals(StatementType.STATEMENT_TYPE_DELETE, TelemetryManager.toStatementType(QueryStatistics.StatementType.DELETE)); + assertEquals(StatementType.STATEMENT_TYPE_MERGE, TelemetryManager.toStatementType(QueryStatistics.StatementType.MERGE)); + assertEquals(StatementType.STATEMENT_TYPE_CREATE_TABLE, TelemetryManager.toStatementType(QueryStatistics.StatementType.CREATE_TABLE)); + assertEquals(StatementType.STATEMENT_TYPE_CREATE_MODEL, TelemetryManager.toStatementType(QueryStatistics.StatementType.CREATE_MODEL)); + assertEquals(StatementType.STATEMENT_TYPE_CREATE_VIEW, TelemetryManager.toStatementType(QueryStatistics.StatementType.CREATE_VIEW)); + assertEquals(StatementType.STATEMENT_TYPE_DROP_TABLE, TelemetryManager.toStatementType(QueryStatistics.StatementType.DROP_TABLE)); + assertEquals(StatementType.STATEMENT_TYPE_DROP_VIEW, TelemetryManager.toStatementType(QueryStatistics.StatementType.DROP_VIEW)); + assertEquals(StatementType.STATEMENT_TYPE_ALTER_TABLE, TelemetryManager.toStatementType(QueryStatistics.StatementType.ALTER_TABLE)); + assertEquals(StatementType.STATEMENT_TYPE_ALTER_VIEW, TelemetryManager.toStatementType(QueryStatistics.StatementType.ALTER_VIEW)); + assertEquals(StatementType.STATEMENT_TYPE_SCRIPT, TelemetryManager.toStatementType(QueryStatistics.StatementType.SCRIPT)); + + assertEquals(StatementType.STATEMENT_TYPE_UNSPECIFIED, TelemetryManager.toStatementType(null)); + } + + @Test + public void testToAuthenticationType() { + assertEquals(AuthenticationType.AUTHENTICATION_TYPE_SERVICE_ACCOUNT, TelemetryManager.toAuthenticationType(0)); + assertEquals(AuthenticationType.AUTHENTICATION_TYPE_USER_AUTHENTICATION, TelemetryManager.toAuthenticationType(1)); + assertEquals(AuthenticationType.AUTHENTICATION_TYPE_APPLICATION_DEFAULT_CREDENTIALS, TelemetryManager.toAuthenticationType(2)); + assertEquals(AuthenticationType.AUTHENTICATION_TYPE_EXTERNAL, TelemetryManager.toAuthenticationType(3)); + assertEquals(AuthenticationType.AUTHENTICATION_TYPE_TOKEN, TelemetryManager.toAuthenticationType(4)); + + assertEquals(AuthenticationType.AUTHENTICATION_TYPE_CUSTOM, TelemetryManager.toAuthenticationType(5)); + assertEquals(AuthenticationType.AUTHENTICATION_TYPE_CUSTOM, TelemetryManager.toAuthenticationType(100)); + } + + @Test + public void testToDurationBucketMs() { + DurationHistogram h1 = TelemetryManager.toDurationBucketMs(5); // < 10 + assertEquals(1, h1.getCount()); + assertEquals(5.0, h1.getSum()); + assertEquals(1, h1.getBucketCounts(0)); // 0th bucket (bounds 10) + + DurationHistogram h2 = TelemetryManager.toDurationBucketMs(150); // < 250, > 100 + assertEquals(1, h2.getBucketCounts(3)); // 250 bound is index 3 + + DurationHistogram h3 = TelemetryManager.toDurationBucketMs(20000); // Overflow > 10000 + assertEquals(1, h3.getBucketCounts(8)); // 8th bucket (overflow) + } + + @Test + public void testOptOutConfiguration() { + Properties props = new Properties(); + + // Default is true + assertTrue(TelemetryConfiguration.builder().resolveEnabledFlag(props).build().isEnabled()); + + // Disabled via lowercase + props.setProperty("enableDiagnosticTelemetry", "false"); + assertFalse(TelemetryConfiguration.builder().resolveEnabledFlag(props).build().isEnabled()); + + // Disabled via uppercase and "0" + props.clear(); + props.setProperty("EnableDiagnosticTelemetry", "0"); + assertFalse(TelemetryConfiguration.builder().resolveEnabledFlag(props).build().isEnabled()); + + // Enabled via "1" + props.clear(); + props.setProperty("EnableDiagnosticTelemetry", "1"); + assertTrue(TelemetryConfiguration.builder().resolveEnabledFlag(props).build().isEnabled()); + } } From 3d5c91e547bdc250d2a4ea1d6051695f1a69d31e Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Thu, 27 Aug 2026 11:29:44 -0400 Subject: [PATCH 2/4] add statement types --- .../jdbc/telemetry/v1/TelemetryBatcher.java | 16 +- .../jdbc/telemetry/v1/TelemetryManager.java | 215 ++++++++++++------ .../jdbc/telemetry/v1/telemetry.proto | 50 +++- .../telemetry/v1/TelemetryBatcherTest.java | 3 +- .../telemetry/v1/TelemetryManagerTest.java | 92 +++++--- 5 files changed, 267 insertions(+), 109 deletions(-) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryBatcher.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryBatcher.java index 80f1dc4352e4..c0a1d9932767 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryBatcher.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryBatcher.java @@ -208,7 +208,12 @@ private void populatePayloadBuilder(TelemetryPayload.Builder builder, List { - TelemetryManager mgr = instance; - if (mgr != null && mgr.getBatcher() != null) { - mgr.getBatcher().offerConnectionAttempt( - ConnectionAttempt.newBuilder() - .setStatus(status) - .setErrorCode(errorCode) - .setAuthType(authType) - .setCount(1) - .build()); - } - }); + runSafely( + () -> { + TelemetryManager mgr = instance; + if (mgr != null && mgr.getBatcher() != null) { + mgr.getBatcher() + .offerConnectionAttempt( + ConnectionAttempt.newBuilder() + .setStatus(status) + .setErrorCode(errorCode) + .setAuthType(authType) + .setCount(1) + .build()); + } + }); } - static void recordStatementExecution(StatementType statementType, QueryApiType apiType, Status status, int errorCode, long durationMs) { - runSafely(() -> { - TelemetryManager mgr = instance; - if (mgr != null && mgr.getBatcher() != null) { - mgr.getBatcher().offerStatementExecution( - StatementExecution.newBuilder() - .setStatementType(statementType) - .setQueryApiType(apiType) - .setStatus(status) - .setErrorCode(errorCode) - .setCount(1) - .setDuration(toDurationBucketMs(durationMs)) - .build()); - } - }); + static void recordStatementExecution( + StatementType statementType, + QueryApiType apiType, + Status status, + int errorCode, + long durationMs) { + runSafely( + () -> { + TelemetryManager mgr = instance; + if (mgr != null && mgr.getBatcher() != null) { + mgr.getBatcher() + .offerStatementExecution( + StatementExecution.newBuilder() + .setStatementType(statementType) + .setQueryApiType(apiType) + .setStatus(status) + .setErrorCode(errorCode) + .setCount(1) + .setDuration(toDurationBucketMs(durationMs)) + .build()); + } + }); } static void recordFeatureUsage(DriverFeature feature, String customFeatureName) { - runSafely(() -> { - TelemetryManager mgr = instance; - if (mgr != null && mgr.getBatcher() != null) { - mgr.getBatcher().offerFeatureUsage( - FeatureUsage.newBuilder() - .setDriverFeature(feature) - .setCustomFeatureName(customFeatureName == null ? "" : customFeatureName) - .setCount(1) - .build()); - } - }); + runSafely( + () -> { + TelemetryManager mgr = instance; + if (mgr != null && mgr.getBatcher() != null) { + mgr.getBatcher() + .offerFeatureUsage( + FeatureUsage.newBuilder() + .setDriverFeature(feature) + .setCustomFeatureName(customFeatureName == null ? "" : customFeatureName) + .setCount(1) + .build()); + } + }); } } diff --git a/java-bigquery-jdbc/src/main/proto/google/cloud/bigquery/jdbc/telemetry/v1/telemetry.proto b/java-bigquery-jdbc/src/main/proto/google/cloud/bigquery/jdbc/telemetry/v1/telemetry.proto index b0d1f9a19d01..177147d42d78 100644 --- a/java-bigquery-jdbc/src/main/proto/google/cloud/bigquery/jdbc/telemetry/v1/telemetry.proto +++ b/java-bigquery-jdbc/src/main/proto/google/cloud/bigquery/jdbc/telemetry/v1/telemetry.proto @@ -89,18 +89,44 @@ enum StatementType { STATEMENT_TYPE_INSERT = 2; STATEMENT_TYPE_UPDATE = 3; STATEMENT_TYPE_DELETE = 4; - STATEMENT_TYPE_MERGE = 5; - STATEMENT_TYPE_CREATE_TABLE = 6; - STATEMENT_TYPE_CREATE_MODEL = 7; - STATEMENT_TYPE_CREATE_VIEW = 8; - STATEMENT_TYPE_DROP_TABLE = 9; - STATEMENT_TYPE_DROP_VIEW = 10; - STATEMENT_TYPE_ALTER_TABLE = 11; - STATEMENT_TYPE_ALTER_VIEW = 12; - STATEMENT_TYPE_SCRIPT = 13; - STATEMENT_TYPE_CALL = 14; - STATEMENT_TYPE_EXPLAIN = 15; - STATEMENT_TYPE_OTHER = 16; + STATEMENT_TYPE_CREATE_EXTERNAL_TABLE = 5; + STATEMENT_TYPE_CREATE_FUNCTION = 6; + STATEMENT_TYPE_CREATE_MATERIALIZED_VIEW = 7; + STATEMENT_TYPE_CREATE_MODEL = 8; + STATEMENT_TYPE_CREATE_PROCEDURE = 9; + STATEMENT_TYPE_CREATE_ROW_ACCESS_POLICY = 10; + STATEMENT_TYPE_CREATE_SCHEMA = 11; + STATEMENT_TYPE_CREATE_SEARCH_INDEX = 12; + STATEMENT_TYPE_CREATE_SNAPSHOT_TABLE = 13; + STATEMENT_TYPE_CREATE_TABLE = 14; + STATEMENT_TYPE_CREATE_TABLE_AS_SELECT = 15; + STATEMENT_TYPE_CREATE_TABLE_FUNCTION = 16; + STATEMENT_TYPE_CREATE_VIEW = 17; + STATEMENT_TYPE_DROP_EXTERNAL_TABLE = 18; + STATEMENT_TYPE_DROP_FUNCTION = 19; + STATEMENT_TYPE_DROP_MATERIALIZED_VIEW = 20; + STATEMENT_TYPE_DROP_MODEL = 21; + STATEMENT_TYPE_DROP_PROCEDURE = 22; + STATEMENT_TYPE_DROP_ROW_ACCESS_POLICY = 23; + STATEMENT_TYPE_DROP_SCHEMA = 24; + STATEMENT_TYPE_DROP_SEARCH_INDEX = 25; + STATEMENT_TYPE_DROP_SNAPSHOT_TABLE = 26; + STATEMENT_TYPE_DROP_TABLE = 27; + STATEMENT_TYPE_DROP_TABLE_FUNCTION = 28; + STATEMENT_TYPE_DROP_VIEW = 29; + STATEMENT_TYPE_ALTER_MATERIALIZED_VIEW = 30; + STATEMENT_TYPE_ALTER_SCHEMA = 31; + STATEMENT_TYPE_ALTER_TABLE = 32; + STATEMENT_TYPE_ALTER_VIEW = 33; + STATEMENT_TYPE_CALL = 34; + STATEMENT_TYPE_EXPLAIN = 35; + STATEMENT_TYPE_EXPORT_DATA = 36; + STATEMENT_TYPE_EXPORT_MODEL = 37; + STATEMENT_TYPE_LOAD_DATA = 38; + STATEMENT_TYPE_MERGE = 39; + STATEMENT_TYPE_SCRIPT = 40; + STATEMENT_TYPE_TRUNCATE_TABLE = 41; + STATEMENT_TYPE_OTHER = 42; } // API substrate used during query execution (REST API vs Read API vs Write API vs Jobless Query). diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryBatcherTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryBatcherTest.java index bf63749705eb..7014e6a699fa 100644 --- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryBatcherTest.java +++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryBatcherTest.java @@ -86,8 +86,7 @@ public LowLevelHttpResponse execute() { ConnectionAttempt.newBuilder().setStatus(Status.STATUS_SUCCESS).build()); batcher.offerStatementExecution( StatementExecution.newBuilder().setStatus(Status.STATUS_SUCCESS).build()); - batcher.offerErrorMetric( - ErrorMetric.newBuilder().setErrorCode(1).setCount(1).build()); + batcher.offerErrorMetric(ErrorMetric.newBuilder().setErrorCode(1).setCount(1).build()); batcher.offerFeatureUsage( FeatureUsage.newBuilder().setDriverFeature(DriverFeature.DRIVER_FEATURE_CUSTOM).build()); diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManagerTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManagerTest.java index dc26ef68a6a7..4667586773e4 100644 --- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManagerTest.java +++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManagerTest.java @@ -108,33 +108,69 @@ public void testRunSafely_handlesNullActionGracefully() { @Test public void testToStatementType() { - assertEquals(StatementType.STATEMENT_TYPE_SELECT, TelemetryManager.toStatementType(QueryStatistics.StatementType.SELECT)); - assertEquals(StatementType.STATEMENT_TYPE_INSERT, TelemetryManager.toStatementType(QueryStatistics.StatementType.INSERT)); - assertEquals(StatementType.STATEMENT_TYPE_UPDATE, TelemetryManager.toStatementType(QueryStatistics.StatementType.UPDATE)); - assertEquals(StatementType.STATEMENT_TYPE_DELETE, TelemetryManager.toStatementType(QueryStatistics.StatementType.DELETE)); - assertEquals(StatementType.STATEMENT_TYPE_MERGE, TelemetryManager.toStatementType(QueryStatistics.StatementType.MERGE)); - assertEquals(StatementType.STATEMENT_TYPE_CREATE_TABLE, TelemetryManager.toStatementType(QueryStatistics.StatementType.CREATE_TABLE)); - assertEquals(StatementType.STATEMENT_TYPE_CREATE_MODEL, TelemetryManager.toStatementType(QueryStatistics.StatementType.CREATE_MODEL)); - assertEquals(StatementType.STATEMENT_TYPE_CREATE_VIEW, TelemetryManager.toStatementType(QueryStatistics.StatementType.CREATE_VIEW)); - assertEquals(StatementType.STATEMENT_TYPE_DROP_TABLE, TelemetryManager.toStatementType(QueryStatistics.StatementType.DROP_TABLE)); - assertEquals(StatementType.STATEMENT_TYPE_DROP_VIEW, TelemetryManager.toStatementType(QueryStatistics.StatementType.DROP_VIEW)); - assertEquals(StatementType.STATEMENT_TYPE_ALTER_TABLE, TelemetryManager.toStatementType(QueryStatistics.StatementType.ALTER_TABLE)); - assertEquals(StatementType.STATEMENT_TYPE_ALTER_VIEW, TelemetryManager.toStatementType(QueryStatistics.StatementType.ALTER_VIEW)); - assertEquals(StatementType.STATEMENT_TYPE_SCRIPT, TelemetryManager.toStatementType(QueryStatistics.StatementType.SCRIPT)); - + assertEquals( + StatementType.STATEMENT_TYPE_SELECT, + TelemetryManager.toStatementType(QueryStatistics.StatementType.SELECT)); + assertEquals( + StatementType.STATEMENT_TYPE_INSERT, + TelemetryManager.toStatementType(QueryStatistics.StatementType.INSERT)); + assertEquals( + StatementType.STATEMENT_TYPE_UPDATE, + TelemetryManager.toStatementType(QueryStatistics.StatementType.UPDATE)); + assertEquals( + StatementType.STATEMENT_TYPE_DELETE, + TelemetryManager.toStatementType(QueryStatistics.StatementType.DELETE)); + assertEquals( + StatementType.STATEMENT_TYPE_MERGE, + TelemetryManager.toStatementType(QueryStatistics.StatementType.MERGE)); + assertEquals( + StatementType.STATEMENT_TYPE_CREATE_TABLE, + TelemetryManager.toStatementType(QueryStatistics.StatementType.CREATE_TABLE)); + assertEquals( + StatementType.STATEMENT_TYPE_CREATE_MODEL, + TelemetryManager.toStatementType(QueryStatistics.StatementType.CREATE_MODEL)); + assertEquals( + StatementType.STATEMENT_TYPE_CREATE_VIEW, + TelemetryManager.toStatementType(QueryStatistics.StatementType.CREATE_VIEW)); + assertEquals( + StatementType.STATEMENT_TYPE_DROP_TABLE, + TelemetryManager.toStatementType(QueryStatistics.StatementType.DROP_TABLE)); + assertEquals( + StatementType.STATEMENT_TYPE_DROP_VIEW, + TelemetryManager.toStatementType(QueryStatistics.StatementType.DROP_VIEW)); + assertEquals( + StatementType.STATEMENT_TYPE_ALTER_TABLE, + TelemetryManager.toStatementType(QueryStatistics.StatementType.ALTER_TABLE)); + assertEquals( + StatementType.STATEMENT_TYPE_ALTER_VIEW, + TelemetryManager.toStatementType(QueryStatistics.StatementType.ALTER_VIEW)); + assertEquals( + StatementType.STATEMENT_TYPE_SCRIPT, + TelemetryManager.toStatementType(QueryStatistics.StatementType.SCRIPT)); + assertEquals(StatementType.STATEMENT_TYPE_UNSPECIFIED, TelemetryManager.toStatementType(null)); } @Test public void testToAuthenticationType() { - assertEquals(AuthenticationType.AUTHENTICATION_TYPE_SERVICE_ACCOUNT, TelemetryManager.toAuthenticationType(0)); - assertEquals(AuthenticationType.AUTHENTICATION_TYPE_USER_AUTHENTICATION, TelemetryManager.toAuthenticationType(1)); - assertEquals(AuthenticationType.AUTHENTICATION_TYPE_APPLICATION_DEFAULT_CREDENTIALS, TelemetryManager.toAuthenticationType(2)); - assertEquals(AuthenticationType.AUTHENTICATION_TYPE_EXTERNAL, TelemetryManager.toAuthenticationType(3)); - assertEquals(AuthenticationType.AUTHENTICATION_TYPE_TOKEN, TelemetryManager.toAuthenticationType(4)); - - assertEquals(AuthenticationType.AUTHENTICATION_TYPE_CUSTOM, TelemetryManager.toAuthenticationType(5)); - assertEquals(AuthenticationType.AUTHENTICATION_TYPE_CUSTOM, TelemetryManager.toAuthenticationType(100)); + assertEquals( + AuthenticationType.AUTHENTICATION_TYPE_SERVICE_ACCOUNT, + TelemetryManager.toAuthenticationType(0)); + assertEquals( + AuthenticationType.AUTHENTICATION_TYPE_USER_AUTHENTICATION, + TelemetryManager.toAuthenticationType(1)); + assertEquals( + AuthenticationType.AUTHENTICATION_TYPE_APPLICATION_DEFAULT_CREDENTIALS, + TelemetryManager.toAuthenticationType(2)); + assertEquals( + AuthenticationType.AUTHENTICATION_TYPE_EXTERNAL, TelemetryManager.toAuthenticationType(3)); + assertEquals( + AuthenticationType.AUTHENTICATION_TYPE_TOKEN, TelemetryManager.toAuthenticationType(4)); + + assertEquals( + AuthenticationType.AUTHENTICATION_TYPE_CUSTOM, TelemetryManager.toAuthenticationType(5)); + assertEquals( + AuthenticationType.AUTHENTICATION_TYPE_CUSTOM, TelemetryManager.toAuthenticationType(100)); } @Test @@ -143,10 +179,10 @@ public void testToDurationBucketMs() { assertEquals(1, h1.getCount()); assertEquals(5.0, h1.getSum()); assertEquals(1, h1.getBucketCounts(0)); // 0th bucket (bounds 10) - + DurationHistogram h2 = TelemetryManager.toDurationBucketMs(150); // < 250, > 100 assertEquals(1, h2.getBucketCounts(3)); // 250 bound is index 3 - + DurationHistogram h3 = TelemetryManager.toDurationBucketMs(20000); // Overflow > 10000 assertEquals(1, h3.getBucketCounts(8)); // 8th bucket (overflow) } @@ -154,19 +190,19 @@ public void testToDurationBucketMs() { @Test public void testOptOutConfiguration() { Properties props = new Properties(); - + // Default is true assertTrue(TelemetryConfiguration.builder().resolveEnabledFlag(props).build().isEnabled()); - + // Disabled via lowercase props.setProperty("enableDiagnosticTelemetry", "false"); assertFalse(TelemetryConfiguration.builder().resolveEnabledFlag(props).build().isEnabled()); - + // Disabled via uppercase and "0" props.clear(); props.setProperty("EnableDiagnosticTelemetry", "0"); assertFalse(TelemetryConfiguration.builder().resolveEnabledFlag(props).build().isEnabled()); - + // Enabled via "1" props.clear(); props.setProperty("EnableDiagnosticTelemetry", "1"); From 4f453880ebdb52f5ff8556da4cf305075161bb78 Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Thu, 27 Aug 2026 15:47:22 -0400 Subject: [PATCH 3/4] address comments --- .../telemetry/v1/TelemetryConfiguration.java | 43 ++++++- .../jdbc/telemetry/v1/TelemetryManager.java | 120 +++++------------- .../telemetry/v1/TelemetryManagerTest.java | 29 ++++- 3 files changed, 96 insertions(+), 96 deletions(-) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryConfiguration.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryConfiguration.java index 4f57507fbc78..4710855efda4 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryConfiguration.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryConfiguration.java @@ -17,6 +17,7 @@ package com.google.cloud.bigquery.jdbc.telemetry.v1; import java.util.Objects; +import java.util.Properties; /** Configuration settings for the BigQuery JDBC driver telemetry client. */ final class TelemetryConfiguration { @@ -156,7 +157,7 @@ Builder setDriverEnvironment(DriverEnvironment driverEnvironment) { return this; } - Builder resolveEnabledFlag(java.util.Properties connectionProperties) { + Builder resolveProperties(Properties connectionProperties) { if (connectionProperties != null) { String propValue = connectionProperties.getProperty("EnableDiagnosticTelemetry"); if (propValue == null) { @@ -165,21 +166,51 @@ Builder resolveEnabledFlag(java.util.Properties connectionProperties) { if (propValue != null) { if ("0".equals(propValue) || "false".equalsIgnoreCase(propValue)) { this.enabled = false; - return this; - } - if ("1".equals(propValue) || "true".equalsIgnoreCase(propValue)) { + } else if ("1".equals(propValue) || "true".equalsIgnoreCase(propValue)) { this.enabled = true; - return this; + } + } + + String uploadIntervalStr = connectionProperties.getProperty("TelemetryUploadInterval"); + if (uploadIntervalStr != null) { + try { + this.uploadIntervalMs = Long.parseLong(uploadIntervalStr); + } catch (NumberFormatException ignored) { + } + } + + String batchSizeStr = connectionProperties.getProperty("TelemetryBatchSize"); + if (batchSizeStr != null) { + try { + this.batchSizeThreshold = Integer.parseInt(batchSizeStr); + } catch (NumberFormatException ignored) { } } } + String envValue = System.getenv("GOOGLE_CLOUD_TELEMETRY_ENABLED"); if (envValue != null) { if ("0".equals(envValue) || "false".equalsIgnoreCase(envValue)) { this.enabled = false; - return this; } } + + String envInterval = System.getenv("GOOGLE_CLOUD_TELEMETRY_UPLOAD_INTERVAL"); + if (envInterval != null) { + try { + this.uploadIntervalMs = Long.parseLong(envInterval); + } catch (NumberFormatException ignored) { + } + } + + String envBatch = System.getenv("GOOGLE_CLOUD_TELEMETRY_BATCH_SIZE"); + if (envBatch != null) { + try { + this.batchSizeThreshold = Integer.parseInt(envBatch); + } catch (NumberFormatException ignored) { + } + } + return this; } diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManager.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManager.java index 6ffc134df0ee..61116dafc1ac 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManager.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManager.java @@ -18,6 +18,8 @@ import com.google.cloud.bigquery.JobStatistics.QueryStatistics; import com.google.cloud.bigquery.jdbc.BigQueryJdbcCustomLogger; +import com.google.protobuf.Descriptors.EnumValueDescriptor; +import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; @@ -33,6 +35,7 @@ final class TelemetryManager implements AutoCloseable { new BigQueryJdbcCustomLogger(TelemetryManager.class.getName()); private static volatile TelemetryManager instance; + private static volatile boolean globallyDisabled = false; private final TelemetryBatcher batcher; @@ -48,14 +51,33 @@ static TelemetryManager getInstance() { return getInstance(null); } - static TelemetryManager getInstance(java.util.Properties properties) { + static TelemetryManager getInstance(Properties properties) { + if (globallyDisabled) { + return null; + } + + if (properties != null) { + TelemetryConfiguration configCheck = + TelemetryConfiguration.builder().resolveProperties(properties).build(); + if (!configCheck.isEnabled()) { + synchronized (TelemetryManager.class) { + globallyDisabled = true; + closeInstance(); + } + return null; + } + } + TelemetryManager localRef = instance; if (localRef == null) { synchronized (TelemetryManager.class) { + if (globallyDisabled) { + return null; + } localRef = instance; if (localRef == null) { TelemetryConfiguration config = - TelemetryConfiguration.builder().resolveEnabledFlag(properties).build(); + TelemetryConfiguration.builder().resolveProperties(properties).build(); ClearcutTransport transport = new ClearcutTransport(config); TelemetryBatcher batcher = new TelemetryBatcher(config, transport); localRef = new TelemetryManager(batcher); @@ -123,94 +145,20 @@ public void close() { } } + // Package-private test helper to reset the global kill switch between test runs + static synchronized void resetGlobalDisableForTest() { + globallyDisabled = false; + } + static StatementType toStatementType(QueryStatistics.StatementType bqStatementType) { if (bqStatementType == null) { return StatementType.STATEMENT_TYPE_UNSPECIFIED; } - switch (bqStatementType.name()) { - case "SELECT": - return StatementType.STATEMENT_TYPE_SELECT; - case "INSERT": - return StatementType.STATEMENT_TYPE_INSERT; - case "UPDATE": - return StatementType.STATEMENT_TYPE_UPDATE; - case "DELETE": - return StatementType.STATEMENT_TYPE_DELETE; - case "MERGE": - return StatementType.STATEMENT_TYPE_MERGE; - case "CREATE_TABLE": - return StatementType.STATEMENT_TYPE_CREATE_TABLE; - case "CREATE_TABLE_AS_SELECT": - return StatementType.STATEMENT_TYPE_CREATE_TABLE_AS_SELECT; - case "CREATE_MODEL": - return StatementType.STATEMENT_TYPE_CREATE_MODEL; - case "CREATE_VIEW": - return StatementType.STATEMENT_TYPE_CREATE_VIEW; - case "CREATE_FUNCTION": - return StatementType.STATEMENT_TYPE_CREATE_FUNCTION; - case "CREATE_PROCEDURE": - return StatementType.STATEMENT_TYPE_CREATE_PROCEDURE; - case "CREATE_MATERIALIZED_VIEW": - return StatementType.STATEMENT_TYPE_CREATE_MATERIALIZED_VIEW; - case "CREATE_TABLE_FUNCTION": - return StatementType.STATEMENT_TYPE_CREATE_TABLE_FUNCTION; - case "CREATE_ROW_ACCESS_POLICY": - return StatementType.STATEMENT_TYPE_CREATE_ROW_ACCESS_POLICY; - case "CREATE_SCHEMA": - return StatementType.STATEMENT_TYPE_CREATE_SCHEMA; - case "CREATE_SNAPSHOT_TABLE": - return StatementType.STATEMENT_TYPE_CREATE_SNAPSHOT_TABLE; - case "CREATE_SEARCH_INDEX": - return StatementType.STATEMENT_TYPE_CREATE_SEARCH_INDEX; - case "CREATE_EXTERNAL_TABLE": - return StatementType.STATEMENT_TYPE_CREATE_EXTERNAL_TABLE; - case "DROP_TABLE": - return StatementType.STATEMENT_TYPE_DROP_TABLE; - case "DROP_VIEW": - return StatementType.STATEMENT_TYPE_DROP_VIEW; - case "DROP_FUNCTION": - return StatementType.STATEMENT_TYPE_DROP_FUNCTION; - case "DROP_PROCEDURE": - return StatementType.STATEMENT_TYPE_DROP_PROCEDURE; - case "DROP_EXTERNAL_TABLE": - return StatementType.STATEMENT_TYPE_DROP_EXTERNAL_TABLE; - case "DROP_MODEL": - return StatementType.STATEMENT_TYPE_DROP_MODEL; - case "DROP_MATERIALIZED_VIEW": - return StatementType.STATEMENT_TYPE_DROP_MATERIALIZED_VIEW; - case "DROP_TABLE_FUNCTION": - return StatementType.STATEMENT_TYPE_DROP_TABLE_FUNCTION; - case "DROP_SEARCH_INDEX": - return StatementType.STATEMENT_TYPE_DROP_SEARCH_INDEX; - case "DROP_SCHEMA": - return StatementType.STATEMENT_TYPE_DROP_SCHEMA; - case "DROP_SNAPSHOT_TABLE": - return StatementType.STATEMENT_TYPE_DROP_SNAPSHOT_TABLE; - case "DROP_ROW_ACCESS_POLICY": - return StatementType.STATEMENT_TYPE_DROP_ROW_ACCESS_POLICY; - case "ALTER_TABLE": - return StatementType.STATEMENT_TYPE_ALTER_TABLE; - case "ALTER_VIEW": - return StatementType.STATEMENT_TYPE_ALTER_VIEW; - case "ALTER_MATERIALIZED_VIEW": - return StatementType.STATEMENT_TYPE_ALTER_MATERIALIZED_VIEW; - case "ALTER_SCHEMA": - return StatementType.STATEMENT_TYPE_ALTER_SCHEMA; - case "TRUNCATE_TABLE": - return StatementType.STATEMENT_TYPE_TRUNCATE_TABLE; - case "EXPORT_DATA": - return StatementType.STATEMENT_TYPE_EXPORT_DATA; - case "EXPORT_MODEL": - return StatementType.STATEMENT_TYPE_EXPORT_MODEL; - case "LOAD_DATA": - return StatementType.STATEMENT_TYPE_LOAD_DATA; - case "CALL": - return StatementType.STATEMENT_TYPE_CALL; - case "SCRIPT": - return StatementType.STATEMENT_TYPE_SCRIPT; - default: - return StatementType.STATEMENT_TYPE_OTHER; - } + + EnumValueDescriptor desc = + StatementType.getDescriptor().findValueByName("STATEMENT_TYPE_" + bqStatementType.name()); + + return desc != null ? StatementType.valueOf(desc) : StatementType.STATEMENT_TYPE_OTHER; } static AuthenticationType toAuthenticationType(int oauthType) { diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManagerTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManagerTest.java index 4667586773e4..0398e613dc83 100644 --- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManagerTest.java +++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryManagerTest.java @@ -19,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -35,6 +36,7 @@ public class TelemetryManagerTest { @AfterEach public void cleanUp() { TelemetryManager.closeInstance(); + TelemetryManager.resetGlobalDisableForTest(); } @Test @@ -192,20 +194,39 @@ public void testOptOutConfiguration() { Properties props = new Properties(); // Default is true - assertTrue(TelemetryConfiguration.builder().resolveEnabledFlag(props).build().isEnabled()); + assertTrue(TelemetryConfiguration.builder().resolveProperties(props).build().isEnabled()); // Disabled via lowercase props.setProperty("enableDiagnosticTelemetry", "false"); - assertFalse(TelemetryConfiguration.builder().resolveEnabledFlag(props).build().isEnabled()); + assertFalse(TelemetryConfiguration.builder().resolveProperties(props).build().isEnabled()); // Disabled via uppercase and "0" props.clear(); props.setProperty("EnableDiagnosticTelemetry", "0"); - assertFalse(TelemetryConfiguration.builder().resolveEnabledFlag(props).build().isEnabled()); + assertFalse(TelemetryConfiguration.builder().resolveProperties(props).build().isEnabled()); // Enabled via "1" props.clear(); props.setProperty("EnableDiagnosticTelemetry", "1"); - assertTrue(TelemetryConfiguration.builder().resolveEnabledFlag(props).build().isEnabled()); + assertTrue(TelemetryConfiguration.builder().resolveProperties(props).build().isEnabled()); + } + + @Test + public void testGlobalKillSwitch() { + // Turn it on + java.util.Properties props1 = new java.util.Properties(); + props1.setProperty("EnableDiagnosticTelemetry", "1"); + TelemetryManager mgr1 = TelemetryManager.getInstance(props1); + assertNotNull(mgr1); + + // Any connection passes '0', it gets permanently killed + java.util.Properties props2 = new java.util.Properties(); + props2.setProperty("EnableDiagnosticTelemetry", "0"); + TelemetryManager mgr2 = TelemetryManager.getInstance(props2); + + // The manager should be null and killed globally + assertNull(mgr2); + assertNull(TelemetryManager.getInstance()); + assertNull(TelemetryManager.getInstance(props1)); } } From 3de37e289f40700fbf12985837c690bc0ee7731b Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Thu, 27 Aug 2026 15:57:38 -0400 Subject: [PATCH 4/4] fix property precedence --- .../telemetry/v1/TelemetryConfiguration.java | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryConfiguration.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryConfiguration.java index 4710855efda4..df5c8e2fbad0 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryConfiguration.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/telemetry/v1/TelemetryConfiguration.java @@ -158,6 +158,7 @@ Builder setDriverEnvironment(DriverEnvironment driverEnvironment) { } Builder resolveProperties(Properties connectionProperties) { + // 1. Connection Properties (lowest precedence) if (connectionProperties != null) { String propValue = connectionProperties.getProperty("EnableDiagnosticTelemetry"); if (propValue == null) { @@ -188,10 +189,13 @@ Builder resolveProperties(Properties connectionProperties) { } } - String envValue = System.getenv("GOOGLE_CLOUD_TELEMETRY_ENABLED"); - if (envValue != null) { - if ("0".equals(envValue) || "false".equalsIgnoreCase(envValue)) { + // 2. Environment Variables (overrides connection properties) + String envEnabled = System.getenv("GOOGLE_CLOUD_TELEMETRY_ENABLED"); + if (envEnabled != null) { + if ("0".equals(envEnabled) || "false".equalsIgnoreCase(envEnabled)) { this.enabled = false; + } else if ("1".equals(envEnabled) || "true".equalsIgnoreCase(envEnabled)) { + this.enabled = true; } } @@ -211,6 +215,32 @@ Builder resolveProperties(Properties connectionProperties) { } } + // 3. JVM System Properties (highest precedence) + String sysEnabled = System.getProperty("GOOGLE_CLOUD_TELEMETRY_ENABLED"); + if (sysEnabled != null) { + if ("0".equals(sysEnabled) || "false".equalsIgnoreCase(sysEnabled)) { + this.enabled = false; + } else if ("1".equals(sysEnabled) || "true".equalsIgnoreCase(sysEnabled)) { + this.enabled = true; + } + } + + String sysInterval = System.getProperty("GOOGLE_CLOUD_TELEMETRY_UPLOAD_INTERVAL"); + if (sysInterval != null) { + try { + this.uploadIntervalMs = Long.parseLong(sysInterval); + } catch (NumberFormatException ignored) { + } + } + + String sysBatch = System.getProperty("GOOGLE_CLOUD_TELEMETRY_BATCH_SIZE"); + if (sysBatch != null) { + try { + this.batchSizeThreshold = Integer.parseInt(sysBatch); + } catch (NumberFormatException ignored) { + } + } + return this; }