All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.grpc.opentelemetry.GrpcOpenTelemetry Maven / Gradle / Ivy

/*
 * Copyright 2023 The gRPC Authors
 *
 * 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 io.grpc.opentelemetry;

import static com.google.common.base.Preconditions.checkNotNull;
import static io.grpc.internal.GrpcUtil.IMPLEMENTATION_VERSION;
import static io.grpc.opentelemetry.internal.OpenTelemetryConstants.LATENCY_BUCKETS;
import static io.grpc.opentelemetry.internal.OpenTelemetryConstants.SIZE_BUCKETS;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Stopwatch;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import io.grpc.ExperimentalApi;
import io.grpc.InternalConfigurator;
import io.grpc.InternalConfiguratorRegistry;
import io.grpc.InternalManagedChannelBuilder;
import io.grpc.ManagedChannelBuilder;
import io.grpc.MetricSink;
import io.grpc.ServerBuilder;
import io.grpc.internal.GrpcUtil;
import io.grpc.opentelemetry.internal.OpenTelemetryConstants;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.api.trace.Tracer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *  The entrypoint for OpenTelemetry metrics functionality in gRPC.
 *
 *  

GrpcOpenTelemetry uses {@link io.opentelemetry.api.OpenTelemetry} APIs for instrumentation. * When no SDK is explicitly added no telemetry data will be collected. See * {@code io.opentelemetry.sdk.OpenTelemetrySdk} for information on how to construct the SDK. * */ public final class GrpcOpenTelemetry { private static final Supplier STOPWATCH_SUPPLIER = new Supplier() { @Override public Stopwatch get() { return Stopwatch.createUnstarted(); } }; @VisibleForTesting static boolean ENABLE_OTEL_TRACING = GrpcUtil.getFlag("GRPC_EXPERIMENTAL_ENABLE_OTEL_TRACING", false); private final OpenTelemetry openTelemetrySdk; private final MeterProvider meterProvider; private final Meter meter; private final Map enableMetrics; private final boolean disableDefault; private final OpenTelemetryMetricsResource resource; private final OpenTelemetryMetricsModule openTelemetryMetricsModule; private final OpenTelemetryTracingModule openTelemetryTracingModule; private final List optionalLabels; private final MetricSink sink; public static Builder newBuilder() { return new Builder(); } private GrpcOpenTelemetry(Builder builder) { this.openTelemetrySdk = checkNotNull(builder.openTelemetrySdk, "openTelemetrySdk"); this.meterProvider = checkNotNull(openTelemetrySdk.getMeterProvider(), "meterProvider"); this.meter = this.meterProvider .meterBuilder(OpenTelemetryConstants.INSTRUMENTATION_SCOPE) .setInstrumentationVersion(IMPLEMENTATION_VERSION) .build(); this.enableMetrics = ImmutableMap.copyOf(builder.enableMetrics); this.disableDefault = builder.disableAll; this.resource = createMetricInstruments(meter, enableMetrics, disableDefault); this.optionalLabels = ImmutableList.copyOf(builder.optionalLabels); this.openTelemetryMetricsModule = new OpenTelemetryMetricsModule( STOPWATCH_SUPPLIER, resource, optionalLabels, builder.plugins); this.openTelemetryTracingModule = new OpenTelemetryTracingModule(openTelemetrySdk); this.sink = new OpenTelemetryMetricSink(meter, enableMetrics, disableDefault, optionalLabels); } @VisibleForTesting OpenTelemetry getOpenTelemetryInstance() { return this.openTelemetrySdk; } @VisibleForTesting MeterProvider getMeterProvider() { return this.meterProvider; } @VisibleForTesting Meter getMeter() { return this.meter; } @VisibleForTesting OpenTelemetryMetricsResource getResource() { return this.resource; } @VisibleForTesting Map getEnableMetrics() { return this.enableMetrics; } @VisibleForTesting List getOptionalLabels() { return optionalLabels; } MetricSink getSink() { return sink; } @VisibleForTesting Tracer getTracer() { return this.openTelemetryTracingModule.getTracer(); } /** * Registers GrpcOpenTelemetry globally, applying its configuration to all subsequently created * gRPC channels and servers. */ @ExperimentalApi("https://github.com/grpc/grpc-java/issues/10591") public void registerGlobal() { InternalConfiguratorRegistry.setConfigurators(Collections.singletonList( new InternalConfigurator() { @Override public void configureChannelBuilder(ManagedChannelBuilder channelBuilder) { GrpcOpenTelemetry.this.configureChannelBuilder(channelBuilder); } @Override public void configureServerBuilder(ServerBuilder serverBuilder) { GrpcOpenTelemetry.this.configureServerBuilder(serverBuilder); } })); } /** * Configures the given {@link ManagedChannelBuilder} with OpenTelemetry metrics instrumentation. */ public void configureChannelBuilder(ManagedChannelBuilder builder) { InternalManagedChannelBuilder.addMetricSink(builder, sink); InternalManagedChannelBuilder.interceptWithTarget( builder, openTelemetryMetricsModule::getClientInterceptor); if (ENABLE_OTEL_TRACING) { builder.intercept(openTelemetryTracingModule.getClientInterceptor()); } } /** * Configures the given {@link ServerBuilder} with OpenTelemetry metrics instrumentation. * * @param serverBuilder the server builder to configure */ public void configureServerBuilder(ServerBuilder serverBuilder) { serverBuilder.addStreamTracerFactory(openTelemetryMetricsModule.getServerTracerFactory()); if (ENABLE_OTEL_TRACING) { serverBuilder.addStreamTracerFactory( openTelemetryTracingModule.getServerTracerFactory()); serverBuilder.intercept(openTelemetryTracingModule.getServerSpanPropagationInterceptor()); } } @VisibleForTesting static OpenTelemetryMetricsResource createMetricInstruments(Meter meter, Map enableMetrics, boolean disableDefault) { OpenTelemetryMetricsResource.Builder builder = OpenTelemetryMetricsResource.builder(); if (isMetricEnabled("grpc.client.call.duration", enableMetrics, disableDefault)) { builder.clientCallDurationCounter( meter.histogramBuilder("grpc.client.call.duration") .setUnit("s") .setDescription( "Time taken by gRPC to complete an RPC from application's perspective") .setExplicitBucketBoundariesAdvice(LATENCY_BUCKETS) .build()); } if (isMetricEnabled("grpc.client.attempt.started", enableMetrics, disableDefault)) { builder.clientAttemptCountCounter( meter.counterBuilder("grpc.client.attempt.started") .setUnit("{attempt}") .setDescription("Number of client call attempts started") .build()); } if (isMetricEnabled("grpc.client.attempt.duration", enableMetrics, disableDefault)) { builder.clientAttemptDurationCounter( meter.histogramBuilder( "grpc.client.attempt.duration") .setUnit("s") .setDescription("Time taken to complete a client call attempt") .setExplicitBucketBoundariesAdvice(LATENCY_BUCKETS) .build()); } if (isMetricEnabled("grpc.client.attempt.sent_total_compressed_message_size", enableMetrics, disableDefault)) { builder.clientTotalSentCompressedMessageSizeCounter( meter.histogramBuilder( "grpc.client.attempt.sent_total_compressed_message_size") .setUnit("By") .setDescription("Compressed message bytes sent per client call attempt") .ofLongs() .setExplicitBucketBoundariesAdvice(SIZE_BUCKETS) .build()); } if (isMetricEnabled("grpc.client.attempt.rcvd_total_compressed_message_size", enableMetrics, disableDefault)) { builder.clientTotalReceivedCompressedMessageSizeCounter( meter.histogramBuilder( "grpc.client.attempt.rcvd_total_compressed_message_size") .setUnit("By") .setDescription("Compressed message bytes received per call attempt") .ofLongs() .setExplicitBucketBoundariesAdvice(SIZE_BUCKETS) .build()); } if (isMetricEnabled("grpc.server.call.started", enableMetrics, disableDefault)) { builder.serverCallCountCounter( meter.counterBuilder("grpc.server.call.started") .setUnit("{call}") .setDescription("Number of server calls started") .build()); } if (isMetricEnabled("grpc.server.call.duration", enableMetrics, disableDefault)) { builder.serverCallDurationCounter( meter.histogramBuilder("grpc.server.call.duration") .setUnit("s") .setDescription( "Time taken to complete a call from server transport's perspective") .setExplicitBucketBoundariesAdvice(LATENCY_BUCKETS) .build()); } if (isMetricEnabled("grpc.server.call.sent_total_compressed_message_size", enableMetrics, disableDefault)) { builder.serverTotalSentCompressedMessageSizeCounter( meter.histogramBuilder( "grpc.server.call.sent_total_compressed_message_size") .setUnit("By") .setDescription("Compressed message bytes sent per server call") .ofLongs() .setExplicitBucketBoundariesAdvice(SIZE_BUCKETS) .build()); } if (isMetricEnabled("grpc.server.call.rcvd_total_compressed_message_size", enableMetrics, disableDefault)) { builder.serverTotalReceivedCompressedMessageSizeCounter( meter.histogramBuilder( "grpc.server.call.rcvd_total_compressed_message_size") .setUnit("By") .setDescription("Compressed message bytes received per server call") .ofLongs() .setExplicitBucketBoundariesAdvice(SIZE_BUCKETS) .build()); } return builder.build(); } static boolean isMetricEnabled(String metricName, Map enableMetrics, boolean disableDefault) { Boolean explicitlyEnabled = enableMetrics.get(metricName); if (explicitlyEnabled != null) { return explicitlyEnabled; } return OpenTelemetryMetricsModule.DEFAULT_PER_CALL_METRICS_SET.contains(metricName) && !disableDefault; } /** * Builder for configuring {@link GrpcOpenTelemetry}. */ public static class Builder { private OpenTelemetry openTelemetrySdk = OpenTelemetry.noop(); private final List plugins = new ArrayList<>(); private final Collection optionalLabels = new ArrayList<>(); private final Map enableMetrics = new HashMap<>(); private boolean disableAll; private Builder() {} /** * Sets the {@link io.opentelemetry.api.OpenTelemetry} entrypoint to use. This can be used to * configure OpenTelemetry by returning the instance created by a * {@code io.opentelemetry.sdk.OpenTelemetrySdkBuilder}. */ public Builder sdk(OpenTelemetry sdk) { this.openTelemetrySdk = sdk; return this; } Builder plugin(OpenTelemetryPlugin plugin) { plugins.add(checkNotNull(plugin, "plugin")); return this; } /** * Adds optionalLabelKey to all the metrics that can provide value for the * optionalLabelKey. */ public Builder addOptionalLabel(String optionalLabelKey) { this.optionalLabels.add(optionalLabelKey); return this; } /** * Enables the specified metrics for collection and export. By default, only a subset of * metrics are enabled. */ public Builder enableMetrics(Collection enableMetrics) { for (String metric : enableMetrics) { this.enableMetrics.put(metric, true); } return this; } /** * Disables the specified metrics from being collected and exported. */ public Builder disableMetrics(Collection disableMetrics) { for (String metric : disableMetrics) { this.enableMetrics.put(metric, false); } return this; } /** * Disable all metrics. If set to true all metrics must be explicitly enabled. */ public Builder disableAllMetrics() { this.enableMetrics.clear(); this.disableAll = true; return this; } Builder enableTracing(boolean enable) { ENABLE_OTEL_TRACING = enable; return this; } /** * Returns a new {@link GrpcOpenTelemetry} built with the configuration of this {@link * Builder}. */ public GrpcOpenTelemetry build() { return new GrpcOpenTelemetry(this); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy