Skip to content

Commit b8b4685

Browse files
committed
chore: Add client_hash metric attribute
1 parent 2e1329e commit b8b4685

File tree

3 files changed

+101
-3
lines changed

3 files changed

+101
-3
lines changed

google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInOpenTelemetryMetricsProvider.java

+34-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import com.google.cloud.opentelemetry.detection.AttributeKeys;
2929
import com.google.cloud.opentelemetry.detection.DetectedPlatform;
3030
import com.google.cloud.opentelemetry.detection.GCPPlatformDetector;
31+
import com.google.common.hash.HashFunction;
32+
import com.google.common.hash.Hashing;
3133
import io.opentelemetry.api.OpenTelemetry;
3234
import io.opentelemetry.sdk.OpenTelemetrySdk;
3335
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
@@ -83,11 +85,41 @@ Map<String, String> createClientAttributes(String projectId, String client_name)
8385
// TODO: Replace this with real value.
8486
clientAttributes.put(INSTANCE_CONFIG_ID_KEY.getKey(), "unknown");
8587
clientAttributes.put(CLIENT_NAME_KEY.getKey(), client_name);
86-
clientAttributes.put(CLIENT_UID_KEY.getKey(), getDefaultTaskValue());
87-
clientAttributes.put(CLIENT_HASH_KEY.getKey(), "cloud_spanner_client_raw_metrics");
88+
String clientUid = getDefaultTaskValue();
89+
clientAttributes.put(CLIENT_UID_KEY.getKey(), clientUid);
90+
clientAttributes.put(CLIENT_HASH_KEY.getKey(), generateClientHash(clientUid));
8891
return clientAttributes;
8992
}
9093

94+
// Returns a 6-digit zero-padded all lower case hexadecimal representation
95+
// of hash of the accounting group.
96+
//
97+
// The hash utilizes the 10 most significant bits of the value returned by
98+
// `Hashing.goodFastHash(64).hashBytes()`, so effectively the returned values are
99+
// uniformly distributed in the range [000000, 0003ff].
100+
//
101+
// The primary purpose of this function is to generate a hash value for the
102+
// `client_hash` resource label using `client_uid` metric field.
103+
//
104+
// The range of values is chosen to be small enough to keep the cardinality of
105+
// the Resource targets under control.
106+
//
107+
// Note: If at later time the range needs to be increased, it can be done by
108+
// increasing the value of `kPrefixLength` to up to 24 bits without changing
109+
// the format of the returned value.
110+
static String generateClientHash(String clientUid) {
111+
if (clientUid == null) {
112+
return "000000";
113+
}
114+
115+
HashFunction hashFunction = Hashing.goodFastHash(64);
116+
Long hash = hashFunction.hashBytes(clientUid.getBytes()).asLong();
117+
// Don't change this value without reading above comment
118+
int kPrefixLength = 10;
119+
long shiftedValue = hash >>> (64 - kPrefixLength);
120+
return String.format("%06x", shiftedValue);
121+
}
122+
91123
static String detectClientLocation() {
92124
GCPPlatformDetector detector = GCPPlatformDetector.DEFAULT_INSTANCE;
93125
DetectedPlatform detectedPlatform = detector.detectPlatform();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2024 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.spanner;
18+
19+
import static com.google.common.truth.Truth.assertThat;
20+
21+
import org.junit.Test;
22+
import org.junit.runner.RunWith;
23+
import org.junit.runners.JUnit4;
24+
25+
@RunWith(JUnit4.class)
26+
public class BuiltInOpenTelemetryMetricsProviderTest {
27+
28+
@Test
29+
public void testGenerateClientHashWithSimpleUid() {
30+
String clientUid = "testClient";
31+
verifyHash(BuiltInOpenTelemetryMetricsProvider.generateClientHash(clientUid));
32+
}
33+
34+
@Test
35+
public void testGenerateClientHashWithEmptyUid() {
36+
String clientUid = "";
37+
verifyHash(BuiltInOpenTelemetryMetricsProvider.generateClientHash(clientUid));
38+
}
39+
40+
@Test
41+
public void testGenerateClientHashWithNullUid() {
42+
String clientUid = null;
43+
verifyHash(BuiltInOpenTelemetryMetricsProvider.generateClientHash(clientUid));
44+
}
45+
46+
@Test
47+
public void testGenerateClientHashWithLongUid() {
48+
String clientUid = "aVeryLongUniqueClientIdentifierThatIsUnusuallyLong";
49+
verifyHash(BuiltInOpenTelemetryMetricsProvider.generateClientHash(clientUid));
50+
}
51+
52+
@Test
53+
public void testGenerateClientHashWithSpecialCharacters() {
54+
String clientUid = "273d60f2-5604-42f1-b687-f5f1b975fd07@2316645@test#";
55+
verifyHash(BuiltInOpenTelemetryMetricsProvider.generateClientHash(clientUid));
56+
}
57+
58+
private void verifyHash(String hash) {
59+
// Check if the hash length is 6
60+
assertThat(hash.length()).isEqualTo(6);
61+
// Check if the hash is in the range [000000, 0003ff]
62+
long hashValue = Long.parseLong(hash, 16); // Convert hash from hex to decimal
63+
assertThat(hashValue).isAtLeast(0);
64+
assertThat(hashValue).isAtMost(0x3FF);
65+
}
66+
}

google-cloud-spanner/src/test/java/com/google/cloud/spanner/OpenTelemetryBuiltInMetricsTracerTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public static void setup() {
9999
BuiltInOpenTelemetryMetricsProvider.detectClientLocation())
100100
.put(BuiltInMetricsConstant.CLIENT_NAME_KEY, client_name)
101101
.put(BuiltInMetricsConstant.CLIENT_UID_KEY, attributes.get("client_uid"))
102-
.put(BuiltInMetricsConstant.CLIENT_HASH_KEY, "cloud_spanner_client_raw_metrics")
102+
.put(BuiltInMetricsConstant.CLIENT_HASH_KEY, attributes.get("client_hash"))
103103
.build();
104104
}
105105

0 commit comments

Comments
 (0)