Skip to content

Commit

Permalink
created builtin metrics tracer factory:
Browse files Browse the repository at this point in the history
  • Loading branch information
surbhigarg92 committed Jan 29, 2025
1 parent 3b83448 commit 7c17432
Show file tree
Hide file tree
Showing 12 changed files with 198 additions and 169 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@
import com.google.cloud.opentelemetry.detection.AttributeKeys;
import com.google.cloud.opentelemetry.detection.DetectedPlatform;
import com.google.cloud.opentelemetry.detection.GCPPlatformDetector;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import io.opentelemetry.api.OpenTelemetry;
Expand All @@ -49,81 +46,42 @@
import java.util.logging.Logger;
import javax.annotation.Nullable;

final class BuiltInOpenTelemetryMetricsProvider {
final class BuiltInMetricsProvider {

public static BuiltInOpenTelemetryMetricsProvider INSTANCE =
new BuiltInOpenTelemetryMetricsProvider();
static BuiltInMetricsProvider INSTANCE = new BuiltInMetricsProvider();

private static final Logger logger =
Logger.getLogger(BuiltInOpenTelemetryMetricsProvider.class.getName());

private final Cache<String, Map<String, String>> clientAttributesCache =
CacheBuilder.newBuilder().maximumSize(1000).build();
Logger.getLogger(BuiltInMetricsProvider.class.getName());

private static String taskId;

private OpenTelemetry openTelemetry;

private Map<String, String> clientAttributes;

private boolean isInitialized;

private BuiltInOpenTelemetryMetricsRecorder builtInOpenTelemetryMetricsRecorder;

private BuiltInOpenTelemetryMetricsProvider() {};

void initialize(
String projectId,
String client_name,
@Nullable Credentials credentials,
@Nullable String monitoringHost) {
private BuiltInMetricsProvider() {}

OpenTelemetry getOrCreateOpenTelemetry(
String projectId, @Nullable Credentials credentials, @Nullable String monitoringHost) {
try {
if (!isInitialized) {
this.openTelemetry = createOpenTelemetry(projectId, credentials, monitoringHost);
this.clientAttributes = createClientAttributes(projectId, client_name);
this.builtInOpenTelemetryMetricsRecorder =
new BuiltInOpenTelemetryMetricsRecorder(openTelemetry, clientAttributes);
isInitialized = true;
if (this.openTelemetry == null) {
SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder();
BuiltInOpenTelemetryMetricsView.registerBuiltinMetrics(
SpannerCloudMonitoringExporter.create(projectId, credentials, monitoringHost),
sdkMeterProviderBuilder);
SdkMeterProvider sdkMeterProvider = sdkMeterProviderBuilder.build();
this.openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).build();
Runtime.getRuntime().addShutdownHook(new Thread(sdkMeterProvider::close));
}
} catch (Exception ex) {
return this.openTelemetry;
} catch (IOException ex) {
logger.log(
Level.WARNING,
"Unable to initialize OpenTelemetry object or attributes for client side metrics, will skip exporting client side metrics",
"Unable to get OpenTelemetry object for client side metrics, will skip exporting client side metrics",
ex);
return null;
}
}

@VisibleForTesting
void initialize(
OpenTelemetry openTelemetry,
String projectId,
String client_name,
@Nullable Credentials credentials,
@Nullable String monitoringHost) {
initialize(projectId, client_name, credentials, monitoringHost);
this.builtInOpenTelemetryMetricsRecorder =
new BuiltInOpenTelemetryMetricsRecorder(openTelemetry, clientAttributes);
}

OpenTelemetry getOpenTelemetry() {
return this.openTelemetry;
}

Map<String, String> getClientAttributes() {
return this.clientAttributes;
}

BuiltInOpenTelemetryMetricsRecorder getBuiltInOpenTelemetryMetricsRecorder() {
return this.builtInOpenTelemetryMetricsRecorder;
}

@VisibleForTesting
void reset() {
isInitialized = false;
}

private Map<String, String> createClientAttributes(String projectId, String client_name) {
Map<String, String> createClientAttributes(String projectId, String client_name) {
Map<String, String> clientAttributes = new HashMap<>();
clientAttributes.put(LOCATION_ID_KEY.getKey(), detectClientLocation());
clientAttributes.put(PROJECT_ID_KEY.getKey(), projectId);
Expand All @@ -135,20 +93,6 @@ private Map<String, String> createClientAttributes(String projectId, String clie
return clientAttributes;
}

private OpenTelemetry createOpenTelemetry(
String projectId, @Nullable Credentials credentials, @Nullable String monitoringHost)
throws IOException {
OpenTelemetry openTelemetry;
SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder();
BuiltInOpenTelemetryMetricsView.registerBuiltinMetrics(
SpannerCloudMonitoringExporter.create(projectId, credentials, monitoringHost),
sdkMeterProviderBuilder);
SdkMeterProvider sdkMeterProvider = sdkMeterProviderBuilder.build();
openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).build();
Runtime.getRuntime().addShutdownHook(new Thread(sdkMeterProvider::close));
return openTelemetry;
}

/**
* Generates a 6-digit zero-padded all lower case hexadecimal representation of hash of the
* accounting group. The hash utilizes the 10 most significant bits of the value returned by
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,21 @@
package com.google.cloud.spanner;

import com.google.api.gax.core.GaxProperties;
import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.Meter;
import java.util.HashMap;
import java.util.Map;

/** OpenTelemetry implementation of recording built in metrics. */
public class BuiltInOpenTelemetryMetricsRecorder {
public class BuiltInMetricsRecorder extends OpenTelemetryMetricsRecorder {

private final DoubleHistogram gfeLatencyRecorder;
private final Map<String, String> attributes = new HashMap<>();
// private final Map<String, String> attributes = new HashMap<>();

/**
* Creates the following instruments for the following metrics:
Expand All @@ -42,27 +42,21 @@ public class BuiltInOpenTelemetryMetricsRecorder {
*
* @param openTelemetry OpenTelemetry instance
*/
public BuiltInOpenTelemetryMetricsRecorder(
OpenTelemetry openTelemetry, Map<String, String> clientAttributes) {
if (openTelemetry == null || clientAttributes == null) {
gfeLatencyRecorder = null;
return;
}

public BuiltInMetricsRecorder(OpenTelemetry openTelemetry, String serviceName) {
super(openTelemetry, serviceName);
Meter meter =
openTelemetry
.meterBuilder(BuiltInMetricsConstant.SPANNER_METER_NAME)
.setInstrumentationVersion(GaxProperties.getLibraryVersion(getClass()))
.build();
this.gfeLatencyRecorder =
meter
.histogramBuilder(
BuiltInMetricsConstant.METER_NAME + '/' + BuiltInMetricsConstant.GFE_LATENCIES_NAME)
.histogramBuilder(serviceName + '/' + BuiltInMetricsConstant.GFE_LATENCIES_NAME)
.setDescription(
"Latency between Google's network receiving an RPC and reading back the first byte of the response")
.setUnit("ms")
.build();
this.attributes.putAll(clientAttributes);
// this.attributes.putAll(clientAttributes);
}

/**
Expand All @@ -73,10 +67,7 @@ public BuiltInOpenTelemetryMetricsRecorder(
* @param attributes Map of the attributes to store
*/
public void recordGFELatency(double gfeLatency, Map<String, String> attributes) {
if (gfeLatencyRecorder != null) {
this.attributes.putAll(attributes);
gfeLatencyRecorder.record(gfeLatency, toOtelAttributes(this.attributes));
}
gfeLatencyRecorder.record(gfeLatency, toOtelAttributes(attributes));
}

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright 2024 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
*
* http://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.spanner;

import com.google.api.gax.tracing.MethodName;
import com.google.api.gax.tracing.MetricsTracer;
import java.util.HashMap;
import java.util.Map;

public class BuiltInMetricsTracer extends MetricsTracer {

private final BuiltInMetricsRecorder builtInOpenTelemetryMetricsRecorder;
// These are RPC specific attributes and pertain to a specific API Trace
private final Map<String, String> attributes = new HashMap<>();

public BuiltInMetricsTracer(
MethodName methodName, BuiltInMetricsRecorder builtInOpenTelemetryMetricsRecorder) {
super(methodName, builtInOpenTelemetryMetricsRecorder);
this.builtInOpenTelemetryMetricsRecorder = builtInOpenTelemetryMetricsRecorder;
this.attributes.put(METHOD_ATTRIBUTE, methodName.toString());
this.attributes.put(LANGUAGE_ATTRIBUTE, DEFAULT_LANGUAGE);
}

public void gfeCapture(double gfeLatency) {
this.builtInOpenTelemetryMetricsRecorder.recordGFELatency(gfeLatency, this.attributes);
}

@Override
public void addAttributes(Map<String, String> attributes) {
super.addAttributes(attributes);
this.attributes.putAll(attributes);
};

@Override
public void addAttributes(String key, String value) {
super.addAttributes(key, value);
this.attributes.put(key, value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2024 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
*
* http://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.spanner;

import com.google.api.core.BetaApi;
import com.google.api.core.InternalApi;
import com.google.api.gax.tracing.ApiTracer;
import com.google.api.gax.tracing.ApiTracerFactory;
import com.google.api.gax.tracing.MethodName;
import com.google.api.gax.tracing.MetricsRecorder;
import com.google.api.gax.tracing.MetricsTracer;
import com.google.api.gax.tracing.MetricsTracerFactory;
import com.google.api.gax.tracing.SpanName;
import com.google.common.collect.ImmutableMap;
import java.util.Map;

/**
* A {@link ApiTracerFactory} to build instances of {@link MetricsTracer}.
*
* <p>This class wraps the {@link MetricsRecorder} and pass it to {@link MetricsTracer}. It will be
* used to record metrics in {@link MetricsTracer}.
*
* <p>This class is expected to be initialized once during client initialization.
*/
@BetaApi
@InternalApi
public class BuiltInMetricsTracerFactory extends MetricsTracerFactory {

protected BuiltInMetricsRecorder builtInOpenTelemetryMetricsRecorder;
private final Map<String, String> attributes;

/**
* Pass in a Map of client level attributes which will be added to every single MetricsTracer
* created from the ApiTracerFactory.
*/
public BuiltInMetricsTracerFactory(
BuiltInMetricsRecorder builtInOpenTelemetryMetricsRecorder, Map<String, String> attributes) {
super(builtInOpenTelemetryMetricsRecorder, attributes);
this.builtInOpenTelemetryMetricsRecorder = builtInOpenTelemetryMetricsRecorder;
this.attributes = ImmutableMap.copyOf(attributes);
}

@Override
public ApiTracer newTracer(ApiTracer parent, SpanName spanName, OperationType operationType) {
BuiltInMetricsTracer metricsTracer =
new BuiltInMetricsTracer(
MethodName.of(spanName.getClientName(), spanName.getMethodName()),
builtInOpenTelemetryMetricsRecorder);
attributes.forEach(metricsTracer::addAttributes);
return metricsTracer;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,12 @@ public void addAttributes(Map<String, String> attributes) {
}
}
}

public void addGFELatency(double latency) {
for (ApiTracer child : children) {
if (child instanceof BuiltInMetricsTracer) {
((BuiltInMetricsTracer) child).gfeCapture(latency);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ static List<TimeSeries> convertToSpannerTimeSeries(List<MetricData> collection)

for (MetricData metricData : collection) {
// Get metrics data from GAX library and Spanner library
if (!(metricData.getInstrumentationScopeInfo().getName().equals(GAX_METER_NAME) || metricData.getInstrumentationScopeInfo().getName().equals(SPANNER_METER_NAME))) {
if (!(metricData.getInstrumentationScopeInfo().getName().equals(GAX_METER_NAME)
|| metricData.getInstrumentationScopeInfo().getName().equals(SPANNER_METER_NAME))) {
// Filter out metric data for instruments that are not part of the spanner metrics list
continue;
}
Expand Down
Loading

0 comments on commit 7c17432

Please sign in to comment.