gcp-observability: Populate global interceptors from observability (#9309)

* Populate global interceptors from observability and added stackdriver exporters
This commit is contained in:
DNVindhya 2022-07-14 07:08:00 -07:00 committed by GitHub
parent 49f555192d
commit ef89bd3ac9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 337 additions and 120 deletions

View File

@ -73,24 +73,19 @@ final class GlobalInterceptors {
isGlobalInterceptorsTracersSet = true;
}
/**
* Returns the list of global {@link ClientInterceptor}. If not set, this returns am empty list.
*/
/** Returns the list of global {@link ClientInterceptor}. If not set, this returns null. */
static synchronized List<ClientInterceptor> getClientInterceptors() {
isGlobalInterceptorsTracersGet = true;
return clientInterceptors;
}
/** Returns list of global {@link ServerInterceptor}. If not set, this returns an empty list. */
/** Returns list of global {@link ServerInterceptor}. If not set, this returns null. */
static synchronized List<ServerInterceptor> getServerInterceptors() {
isGlobalInterceptorsTracersGet = true;
return serverInterceptors;
}
/**
* Returns list of global {@link ServerStreamTracer.Factory}. If not set, this returns an empty
* list.
*/
/** Returns list of global {@link ServerStreamTracer.Factory}. If not set, this returns null. */
static synchronized List<ServerStreamTracer.Factory> getServerStreamTracerFactories() {
isGlobalInterceptorsTracersGet = true;
return serverStreamTracerFactories;

View File

@ -30,6 +30,7 @@ dependencies {
project(':grpc-alts'),
project(':grpc-census'),
("com.google.cloud:google-cloud-logging:${cloudLoggingVersion}"),
libraries.opencensus.contrib.grpc.metrics,
libraries.opencensus.exporter.stats.stackdriver,
libraries.opencensus.exporter.trace.stackdriver,
libraries.animalsniffer.annotations, // Prefer our version
@ -41,7 +42,8 @@ dependencies {
runtimeOnly libraries.opencensus.impl
testImplementation project(':grpc-testing'),
testImplementation project(':grpc-context').sourceSets.test.output,
project(':grpc-testing'),
project(':grpc-testing-proto'),
project(':grpc-netty-shaded')
testImplementation (libraries.guava.testlib) {

View File

@ -19,8 +19,14 @@ package io.grpc.gcp.observability;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.VisibleForTesting;
import io.grpc.ClientInterceptor;
import io.grpc.ExperimentalApi;
import io.grpc.InternalGlobalInterceptors;
import io.grpc.ManagedChannelProvider.ProviderNotFoundException;
import io.grpc.ServerInterceptor;
import io.grpc.ServerStreamTracer;
import io.grpc.census.InternalCensusStatsAccessor;
import io.grpc.census.InternalCensusTracingAccessor;
import io.grpc.gcp.observability.interceptors.ConfigFilterHelper;
import io.grpc.gcp.observability.interceptors.InternalLoggingChannelInterceptor;
import io.grpc.gcp.observability.interceptors.InternalLoggingServerInterceptor;
@ -28,13 +34,30 @@ import io.grpc.gcp.observability.interceptors.LogHelper;
import io.grpc.gcp.observability.logging.GcpLogSink;
import io.grpc.gcp.observability.logging.Sink;
import io.grpc.internal.TimeProvider;
import io.opencensus.contrib.grpc.metrics.RpcViews;
import io.opencensus.exporter.stats.stackdriver.StackdriverStatsConfiguration;
import io.opencensus.exporter.stats.stackdriver.StackdriverStatsExporter;
import io.opencensus.exporter.trace.stackdriver.StackdriverTraceConfiguration;
import io.opencensus.exporter.trace.stackdriver.StackdriverTraceExporter;
import io.opencensus.trace.Tracing;
import io.opencensus.trace.config.TraceConfig;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
/** The main class for gRPC Google Cloud Platform Observability features. */
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/8869")
public final class GcpObservability implements AutoCloseable {
private static final Logger logger = Logger.getLogger(GcpObservability.class.getName());
private static GcpObservability instance = null;
private final Sink sink;
private final ObservabilityConfig config;
private final ArrayList<ClientInterceptor> clientInterceptors = new ArrayList<>();
private final ArrayList<ServerInterceptor> serverInterceptors = new ArrayList<>();
private final ArrayList<ServerStreamTracer.Factory> tracerFactories = new ArrayList<>();
private boolean metricsEnabled;
private boolean tracesEnabled;
/**
* Initialize grpc-observability.
@ -48,20 +71,33 @@ public final class GcpObservability implements AutoCloseable {
Sink sink = new GcpLogSink(observabilityConfig.getDestinationProjectId(),
globalLoggingTags.getLocationTags(), globalLoggingTags.getCustomTags(),
observabilityConfig.getFlushMessageCount());
// TODO(dnvindhya): Cleanup code for LoggingChannelProvider and LoggingServerProvider
// once ChannelBuilder and ServerBuilder are used
LogHelper helper = new LogHelper(sink, TimeProvider.SYSTEM_TIME_PROVIDER);
ConfigFilterHelper configFilterHelper = ConfigFilterHelper.factory(observabilityConfig);
instance = grpcInit(sink,
instance = grpcInit(sink, observabilityConfig,
new InternalLoggingChannelInterceptor.FactoryImpl(helper, configFilterHelper),
new InternalLoggingServerInterceptor.FactoryImpl(helper, configFilterHelper));
instance.registerStackDriverExporter(observabilityConfig.getDestinationProjectId());
}
return instance;
}
@VisibleForTesting static GcpObservability grpcInit(Sink sink,
@VisibleForTesting
static GcpObservability grpcInit(
Sink sink,
ObservabilityConfig config,
InternalLoggingChannelInterceptor.Factory channelInterceptorFactory,
InternalLoggingServerInterceptor.Factory serverInterceptorFactory) {
InternalLoggingServerInterceptor.Factory serverInterceptorFactory)
throws IOException {
if (instance == null) {
instance = new GcpObservability(sink, channelInterceptorFactory, serverInterceptorFactory);
instance =
new GcpObservability(sink, config, channelInterceptorFactory, serverInterceptorFactory);
LogHelper logHelper = new LogHelper(sink, TimeProvider.SYSTEM_TIME_PROVIDER);
ConfigFilterHelper logFilterHelper = ConfigFilterHelper.factory(config);
instance.setProducer(
new InternalLoggingChannelInterceptor.FactoryImpl(logHelper, logFilterHelper),
new InternalLoggingServerInterceptor.FactoryImpl(logHelper, logFilterHelper));
}
return instance;
}
@ -73,6 +109,7 @@ public final class GcpObservability implements AutoCloseable {
if (instance == null) {
throw new IllegalStateException("GcpObservability already closed!");
}
unRegisterStackDriverExporter();
LoggingChannelProvider.shutdown();
LoggingServerProvider.shutdown();
sink.close();
@ -80,10 +117,84 @@ public final class GcpObservability implements AutoCloseable {
}
}
private GcpObservability(Sink sink,
private void setProducer(
InternalLoggingChannelInterceptor.Factory channelInterceptorFactory,
InternalLoggingServerInterceptor.Factory serverInterceptorFactory) {
if (config.isEnableCloudLogging()) {
clientInterceptors.add(channelInterceptorFactory.create());
serverInterceptors.add(serverInterceptorFactory.create());
}
if (config.isEnableCloudMonitoring()) {
clientInterceptors.add(
InternalCensusStatsAccessor.getClientInterceptor(true, true, true, true));
tracerFactories.add(
InternalCensusStatsAccessor.getServerStreamTracerFactory(true, true, true));
}
if (config.isEnableCloudTracing()) {
clientInterceptors.add(InternalCensusTracingAccessor.getClientInterceptor());
tracerFactories.add(InternalCensusTracingAccessor.getServerStreamTracerFactory());
}
InternalGlobalInterceptors.setInterceptorsTracers(
clientInterceptors, serverInterceptors, tracerFactories);
}
private void registerStackDriverExporter(String projectId) throws IOException {
if (config.isEnableCloudMonitoring()) {
RpcViews.registerAllGrpcViews();
StackdriverStatsConfiguration.Builder statsConfigurationBuilder =
StackdriverStatsConfiguration.builder();
if (projectId != null) {
statsConfigurationBuilder.setProjectId(projectId);
}
StackdriverStatsExporter.createAndRegister(statsConfigurationBuilder.build());
metricsEnabled = true;
}
if (config.isEnableCloudTracing()) {
TraceConfig traceConfig = Tracing.getTraceConfig();
traceConfig.updateActiveTraceParams(
traceConfig.getActiveTraceParams().toBuilder().setSampler(config.getSampler()).build());
StackdriverTraceConfiguration.Builder traceConfigurationBuilder =
StackdriverTraceConfiguration.builder();
if (projectId != null) {
traceConfigurationBuilder.setProjectId(projectId);
}
StackdriverTraceExporter.createAndRegister(traceConfigurationBuilder.build());
tracesEnabled = true;
}
}
private void unRegisterStackDriverExporter() {
if (metricsEnabled) {
try {
StackdriverStatsExporter.unregister();
} catch (IllegalStateException e) {
logger.log(
Level.SEVERE, "Failed to unregister Stackdriver stats exporter, " + e.getMessage());
}
metricsEnabled = false;
}
if (tracesEnabled) {
try {
StackdriverTraceExporter.unregister();
} catch (IllegalStateException e) {
logger.log(
Level.SEVERE, "Failed to unregister Stackdriver trace exporter, " + e.getMessage());
}
tracesEnabled = false;
}
}
private GcpObservability(
Sink sink,
ObservabilityConfig config,
InternalLoggingChannelInterceptor.Factory channelInterceptorFactory,
InternalLoggingServerInterceptor.Factory serverInterceptorFactory) {
this.sink = checkNotNull(sink);
this.config = checkNotNull(config);
LoggingChannelProvider.init(checkNotNull(channelInterceptorFactory));
LoggingServerProvider.init(checkNotNull(serverInterceptorFactory));
}

View File

@ -18,6 +18,7 @@ package io.grpc.gcp.observability;
import io.grpc.Internal;
import io.grpc.observabilitylog.v1.GrpcLogRecord.EventType;
import io.opencensus.trace.Sampler;
import java.util.List;
@Internal
@ -43,6 +44,7 @@ public interface ObservabilityConfig {
/** Get event types to log. */
List<EventType> getEventTypes();
/** Get sampler for TraceConfig - when Cloud Tracing is enabled. */
Sampler getSampler();
/**
@ -71,34 +73,4 @@ public interface ObservabilityConfig {
this.messageBytes = messageBytes;
}
}
/** Corresponds to a {@link io.opencensus.trace.Sampler} type. */
enum SamplerType {
ALWAYS,
NEVER,
PROBABILISTIC;
}
/** Represents a trace {@link io.opencensus.trace.Sampler} configuration. */
class Sampler {
private SamplerType type;
private double probability;
Sampler(double probability) {
this.probability = probability;
this.type = SamplerType.PROBABILISTIC;
}
Sampler(SamplerType type) {
this.type = type;
}
double getProbability() {
return probability;
}
SamplerType getType() {
return type;
}
}
}

View File

@ -23,6 +23,8 @@ import com.google.common.collect.ImmutableList;
import io.grpc.internal.JsonParser;
import io.grpc.internal.JsonUtil;
import io.grpc.observabilitylog.v1.GrpcLogRecord.EventType;
import io.opencensus.trace.Sampler;
import io.opencensus.trace.samplers.Samplers;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
@ -35,6 +37,8 @@ import java.util.Map;
final class ObservabilityConfigImpl implements ObservabilityConfig {
private static final String CONFIG_ENV_VAR_NAME = "GRPC_CONFIG_OBSERVABILITY";
private static final String CONFIG_FILE_ENV_VAR_NAME = "GRPC_CONFIG_OBSERVABILITY_JSON";
// Tolerance for floating-point comparisons.
private static final double EPSILON = 1e-6;
private boolean enableCloudLogging = false;
private boolean enableCloudMonitoring = false;
@ -100,19 +104,21 @@ final class ObservabilityConfigImpl implements ObservabilityConfig {
}
this.eventTypes = eventTypesBuilder.build();
}
String sampler = JsonUtil.getString(config, "global_trace_sampler");
Double samplingRate = JsonUtil.getNumberAsDouble(config, "global_trace_sampling_rate");
checkArgument(
sampler == null || samplingRate == null,
"only one of 'global_trace_sampler' or 'global_trace_sampling_rate' can be specified");
if (sampler != null) {
this.sampler = new Sampler(SamplerType.valueOf(sampler.toUpperCase()));
}
if (samplingRate != null) {
if (samplingRate == null) {
this.sampler = Samplers.probabilitySampler(0.0);
} else {
checkArgument(
samplingRate >= 0.0 && samplingRate <= 1.0,
"'global_trace_sampling_rate' needs to be between 0.0 and 1.0");
this.sampler = new Sampler(samplingRate);
"'global_trace_sampling_rate' needs to be between [0.0, 1.0]");
// Using alwaysSample() instead of probabilitySampler() because according to
// {@link io.opencensus.trace.samplers.ProbabilitySampler#shouldSample}
// there is a (very) small chance of *not* sampling if probability = 1.00.
if (1 - samplingRate < EPSILON) {
this.sampler = Samplers.alwaysSample();
} else {
this.sampler = Samplers.probabilitySampler(samplingRate);
}
}
}
}

View File

@ -18,48 +18,185 @@ package io.grpc.gcp.observability;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.AdditionalAnswers.delegatesTo;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.InternalGlobalInterceptors;
import io.grpc.ManagedChannelProvider;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
import io.grpc.ServerCall;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.grpc.ServerProvider;
import io.grpc.StaticTestingClassLoader;
import io.grpc.gcp.observability.interceptors.InternalLoggingChannelInterceptor;
import io.grpc.gcp.observability.interceptors.InternalLoggingServerInterceptor;
import io.grpc.gcp.observability.logging.Sink;
import io.opencensus.trace.samplers.Samplers;
import java.io.IOException;
import java.util.regex.Pattern;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class GcpObservabilityTest {
@Test
public void initFinish() {
ManagedChannelProvider prevChannelProvider = ManagedChannelProvider.provider();
ServerProvider prevServerProvider = ServerProvider.provider();
Sink sink = mock(Sink.class);
InternalLoggingChannelInterceptor.Factory channelInterceptorFactory = mock(
InternalLoggingChannelInterceptor.Factory.class);
InternalLoggingServerInterceptor.Factory serverInterceptorFactory = mock(
InternalLoggingServerInterceptor.Factory.class);
GcpObservability observability1;
try (GcpObservability observability = GcpObservability.grpcInit(sink, channelInterceptorFactory,
serverInterceptorFactory)) {
assertThat(ManagedChannelProvider.provider()).isInstanceOf(LoggingChannelProvider.class);
assertThat(ServerProvider.provider()).isInstanceOf(ServerProvider.class);
observability1 = GcpObservability.grpcInit(sink, channelInterceptorFactory,
serverInterceptorFactory);
assertThat(observability1).isSameInstanceAs(observability);
private final StaticTestingClassLoader classLoader =
new StaticTestingClassLoader(
getClass().getClassLoader(),
Pattern.compile(
"io\\.grpc\\.InternalGlobalInterceptors|io\\.grpc\\.GlobalInterceptors|"
+ "io\\.grpc\\.gcp\\.observability\\.[^.]+|"
+ "io\\.grpc\\.gcp\\.observability\\.interceptors\\.[^.]+|"
+ "io\\.grpc\\.gcp\\.observability\\.GcpObservabilityTest\\$.*"));
@Test
public void initFinish() throws Exception {
Class<?> runnable =
classLoader.loadClass(StaticTestingClassInitFinish.class.getName());
((Runnable) runnable.getDeclaredConstructor().newInstance()).run();
}
@Test
public void enableObservability() throws Exception {
Class<?> runnable =
classLoader.loadClass(StaticTestingClassEnableObservability.class.getName());
((Runnable) runnable.getDeclaredConstructor().newInstance()).run();
}
@Test
public void disableObservability() throws Exception {
Class<?> runnable =
classLoader.loadClass(StaticTestingClassDisableObservability.class.getName());
((Runnable) runnable.getDeclaredConstructor().newInstance()).run();
}
// UsedReflectively
public static final class StaticTestingClassInitFinish implements Runnable {
@Override
public void run() {
// TODO(dnvindhya) : Remove usage of Providers on cleaning up Logging*Provider
ManagedChannelProvider prevChannelProvider = ManagedChannelProvider.provider();
ServerProvider prevServerProvider = ServerProvider.provider();
Sink sink = mock(Sink.class);
ObservabilityConfig config = mock(ObservabilityConfig.class);
InternalLoggingChannelInterceptor.Factory channelInterceptorFactory =
mock(InternalLoggingChannelInterceptor.Factory.class);
InternalLoggingServerInterceptor.Factory serverInterceptorFactory =
mock(InternalLoggingServerInterceptor.Factory.class);
GcpObservability observability1;
try {
GcpObservability observability =
GcpObservability.grpcInit(
sink, config, channelInterceptorFactory, serverInterceptorFactory);
assertThat(ManagedChannelProvider.provider()).isInstanceOf(LoggingChannelProvider.class);
assertThat(ServerProvider.provider()).isInstanceOf(ServerProvider.class);
observability1 =
GcpObservability.grpcInit(
sink, config, channelInterceptorFactory, serverInterceptorFactory);
assertThat(observability1).isSameInstanceAs(observability);
observability.close();
verify(sink).close();
assertThat(ManagedChannelProvider.provider()).isSameInstanceAs(prevChannelProvider);
assertThat(ServerProvider.provider()).isSameInstanceAs(prevServerProvider);
try {
observability1.close();
fail("should have failed for calling close() second time");
} catch (IllegalStateException e) {
assertThat(e).hasMessageThat().contains("GcpObservability already closed!");
}
} catch (IOException e) {
fail("Encountered exception: " + e);
}
}
verify(sink).close();
assertThat(ManagedChannelProvider.provider()).isSameInstanceAs(prevChannelProvider);
assertThat(ServerProvider.provider()).isSameInstanceAs(prevServerProvider);
try {
observability1.close();
fail("should have failed for calling close() second time");
} catch (IllegalStateException e) {
assertThat(e).hasMessageThat().contains("GcpObservability already closed!");
}
public static final class StaticTestingClassEnableObservability implements Runnable {
@Override
public void run() {
Sink sink = mock(Sink.class);
ObservabilityConfig config = mock(ObservabilityConfig.class);
when(config.isEnableCloudLogging()).thenReturn(true);
when(config.isEnableCloudMonitoring()).thenReturn(true);
when(config.isEnableCloudTracing()).thenReturn(true);
when(config.getSampler()).thenReturn(Samplers.neverSample());
ClientInterceptor clientInterceptor =
mock(ClientInterceptor.class, delegatesTo(new NoopClientInterceptor()));
InternalLoggingChannelInterceptor.Factory channelInterceptorFactory =
mock(InternalLoggingChannelInterceptor.Factory.class);
when(channelInterceptorFactory.create()).thenReturn(clientInterceptor);
ServerInterceptor serverInterceptor =
mock(ServerInterceptor.class, delegatesTo(new NoopServerInterceptor()));
InternalLoggingServerInterceptor.Factory serverInterceptorFactory =
mock(InternalLoggingServerInterceptor.Factory.class);
when(serverInterceptorFactory.create()).thenReturn(serverInterceptor);
try (GcpObservability unused =
GcpObservability.grpcInit(
sink, config, channelInterceptorFactory, serverInterceptorFactory)) {
assertThat(InternalGlobalInterceptors.getClientInterceptors()).hasSize(3);
assertThat(InternalGlobalInterceptors.getServerInterceptors()).hasSize(1);
assertThat(InternalGlobalInterceptors.getServerStreamTracerFactories()).hasSize(2);
} catch (Exception e) {
fail("Encountered exception: " + e);
}
}
}
public static final class StaticTestingClassDisableObservability implements Runnable {
@Override
public void run() {
Sink sink = mock(Sink.class);
ObservabilityConfig config = mock(ObservabilityConfig.class);
when(config.isEnableCloudLogging()).thenReturn(false);
when(config.isEnableCloudMonitoring()).thenReturn(false);
when(config.isEnableCloudTracing()).thenReturn(false);
when(config.getSampler()).thenReturn(Samplers.neverSample());
InternalLoggingChannelInterceptor.Factory channelInterceptorFactory =
mock(InternalLoggingChannelInterceptor.Factory.class);
InternalLoggingServerInterceptor.Factory serverInterceptorFactory =
mock(InternalLoggingServerInterceptor.Factory.class);;
try (GcpObservability unused =
GcpObservability.grpcInit(
sink, config, channelInterceptorFactory, serverInterceptorFactory)) {
assertThat(InternalGlobalInterceptors.getClientInterceptors()).isEmpty();
assertThat(InternalGlobalInterceptors.getServerInterceptors()).isEmpty();
assertThat(InternalGlobalInterceptors.getServerStreamTracerFactories()).isEmpty();
} catch (Exception e) {
fail("Encountered exception: " + e);
}
verify(sink).close();
}
}
private static class NoopClientInterceptor implements ClientInterceptor {
@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
return next.newCall(method, callOptions);
}
}
private static class NoopServerInterceptor implements ServerInterceptor {
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
return next.startCall(call, headers);
}
}
}

View File

@ -26,7 +26,8 @@ import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import io.grpc.gcp.observability.ObservabilityConfig.LogFilter;
import io.grpc.observabilitylog.v1.GrpcLogRecord.EventType;
import io.opencensus.trace.Sampler;
import io.opencensus.trace.samplers.Samplers;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
@ -77,35 +78,33 @@ public class ObservabilityConfigImplTest {
+ "}";
private static final String ENABLE_CLOUD_MONITORING_AND_TRACING = "{\n"
+ " \"enable_cloud_monitoring\": true,\n"
+ " \"enable_cloud_tracing\": true\n"
+ "}";
+ " \"enable_cloud_monitoring\": true,\n"
+ " \"enable_cloud_tracing\": true\n"
+ "}";
private static final String GLOBAL_TRACING_ALWAYS_SAMPLER = "{\n"
+ " \"enable_cloud_tracing\": true,\n"
+ " \"global_trace_sampler\": \"always\"\n"
+ "}";
+ " \"enable_cloud_tracing\": true,\n"
+ " \"global_trace_sampling_rate\": 1.00\n"
+ "}";
private static final String GLOBAL_TRACING_NEVER_SAMPLER = "{\n"
+ " \"enable_cloud_tracing\": true,\n"
+ " \"global_trace_sampler\": \"never\"\n"
+ "}";
+ " \"enable_cloud_tracing\": true,\n"
+ " \"global_trace_sampling_rate\": 0.00\n"
+ "}";
private static final String GLOBAL_TRACING_PROBABILISTIC_SAMPLER = "{\n"
+ " \"enable_cloud_tracing\": true,\n"
+ " \"global_trace_sampling_rate\": 0.75\n"
+ "}";
+ " \"enable_cloud_tracing\": true,\n"
+ " \"global_trace_sampling_rate\": 0.75\n"
+ "}";
private static final String GLOBAL_TRACING_BOTH_SAMPLER_ERROR = "{\n"
+ " \"enable_cloud_tracing\": true,\n"
+ " \"global_trace_sampler\": \"never\",\n"
+ " \"global_trace_sampling_rate\": 0.75\n"
+ "}";
private static final String GLOBAL_TRACING_DEFAULT_SAMPLER = "{\n"
+ " \"enable_cloud_tracing\": true\n"
+ "}";
private static final String GLOBAL_TRACING_BADPROBABILISTIC_SAMPLER = "{\n"
+ " \"enable_cloud_tracing\": true,\n"
+ " \"global_trace_sampling_rate\": -0.75\n"
+ "}";
+ " \"enable_cloud_tracing\": true,\n"
+ " \"global_trace_sampling_rate\": -0.75\n"
+ "}";
ObservabilityConfigImpl observabilityConfig = new ObservabilityConfigImpl();
@ -198,41 +197,36 @@ public class ObservabilityConfigImplTest {
public void alwaysSampler() throws IOException {
observabilityConfig.parse(GLOBAL_TRACING_ALWAYS_SAMPLER);
assertTrue(observabilityConfig.isEnableCloudTracing());
ObservabilityConfig.Sampler sampler = observabilityConfig.getSampler();
Sampler sampler = observabilityConfig.getSampler();
assertThat(sampler).isNotNull();
assertThat(sampler.getType()).isEqualTo(ObservabilityConfig.SamplerType.ALWAYS);
assertThat(sampler).isEqualTo(Samplers.alwaysSample());
}
@Test
public void neverSampler() throws IOException {
observabilityConfig.parse(GLOBAL_TRACING_NEVER_SAMPLER);
assertTrue(observabilityConfig.isEnableCloudTracing());
ObservabilityConfig.Sampler sampler = observabilityConfig.getSampler();
Sampler sampler = observabilityConfig.getSampler();
assertThat(sampler).isNotNull();
assertThat(sampler.getType()).isEqualTo(ObservabilityConfig.SamplerType.NEVER);
assertThat(sampler).isEqualTo(Samplers.probabilitySampler(0.0));
}
@Test
public void probabilisticSampler() throws IOException {
observabilityConfig.parse(GLOBAL_TRACING_PROBABILISTIC_SAMPLER);
assertTrue(observabilityConfig.isEnableCloudTracing());
ObservabilityConfig.Sampler sampler = observabilityConfig.getSampler();
Sampler sampler = observabilityConfig.getSampler();
assertThat(sampler).isNotNull();
assertThat(sampler.getType()).isEqualTo(ObservabilityConfig.SamplerType.PROBABILISTIC);
assertThat(sampler.getProbability()).isEqualTo(0.75);
assertThat(sampler).isEqualTo(Samplers.probabilitySampler(0.75));
}
@Test
public void bothSamplerAndSamplingRate_error() throws IOException {
try {
observabilityConfig.parse(GLOBAL_TRACING_BOTH_SAMPLER_ERROR);
fail("exception expected!");
} catch (IllegalArgumentException iae) {
assertThat(iae.getMessage())
.isEqualTo(
"only one of 'global_trace_sampler' or 'global_trace_sampling_rate' can be"
+ " specified");
}
public void defaultSampler() throws IOException {
observabilityConfig.parse(GLOBAL_TRACING_DEFAULT_SAMPLER);
assertTrue(observabilityConfig.isEnableCloudTracing());
Sampler sampler = observabilityConfig.getSampler();
assertThat(sampler).isNotNull();
assertThat(sampler).isEqualTo(Samplers.probabilitySampler(0.00));
}
@Test
@ -242,7 +236,7 @@ public class ObservabilityConfigImplTest {
fail("exception expected!");
} catch (IllegalArgumentException iae) {
assertThat(iae.getMessage()).isEqualTo(
"'global_trace_sampling_rate' needs to be between 0.0 and 1.0");
"'global_trace_sampling_rate' needs to be between [0.0, 1.0]");
}
}
@ -263,4 +257,4 @@ public class ObservabilityConfigImplTest {
assertThat(logFilters.get(1).headerBytes).isNull();
assertThat(logFilters.get(1).messageBytes).isNull();
}
}
}