Skip to content

Commit 438f837

Browse files
authored
chore: Add client_hash metric attribute (#3357)
* chore: Add client_hash metric attribute * review comments
1 parent f910f70 commit 438f837

File tree

3 files changed

+100
-3
lines changed

3 files changed

+100
-3
lines changed

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

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

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

Lines changed: 1 addition & 1 deletion
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)