From 4bc0242c17cbaa931f30ef031c312c8ee936b6e0 Mon Sep 17 00:00:00 2001 From: Bogdan Drutu Date: Mon, 12 Jun 2017 13:17:06 -0700 Subject: Move Sampler and StartSpanOptions in base. Create a samplers package for all the Sampler implementations. (#347) --- api/src/main/java/io/opencensus/trace/Sampler.java | 43 ----- .../main/java/io/opencensus/trace/Samplers.java | 168 -------------------- api/src/main/java/io/opencensus/trace/Span.java | 1 + .../main/java/io/opencensus/trace/SpanBuilder.java | 2 + .../java/io/opencensus/trace/StartSpanOptions.java | 121 -------------- api/src/main/java/io/opencensus/trace/Tracer.java | 1 + .../java/io/opencensus/trace/base/Sampler.java | 44 ++++++ .../io/opencensus/trace/base/StartSpanOptions.java | 122 +++++++++++++++ .../io/opencensus/trace/config/TraceParams.java | 20 ++- .../io/opencensus/trace/internal/SpanFactory.java | 2 +- .../trace/samplers/AlwaysSampleSampler.java | 50 ++++++ .../trace/samplers/NeverSampleSampler.java | 50 ++++++ .../trace/samplers/ProbabilitySampler.java | 93 +++++++++++ .../io/opencensus/trace/samplers/Samplers.java | 55 +++++++ .../java/io/opencensus/trace/SamplersTest.java | 170 -------------------- .../java/io/opencensus/trace/SpanBuilderTest.java | 2 + .../io/opencensus/trace/StartSpanOptionsTest.java | 111 ------------- .../test/java/io/opencensus/trace/TracerTest.java | 1 + .../trace/base/StartSpanOptionsTest.java | 114 ++++++++++++++ .../opencensus/trace/config/TraceConfigTest.java | 2 +- .../opencensus/trace/config/TraceParamsTest.java | 2 +- .../io/opencensus/trace/samplers/SamplersTest.java | 173 +++++++++++++++++++++ .../RecordTraceEventsNonSampledSpanBenchmark.java | 1 + .../RecordTraceEventsSampledSpanBenchmark.java | 1 + .../io/opencensus/trace/StartEndSpanBenchmark.java | 1 + .../java/io/opencensus/trace/SpanFactoryImpl.java | 2 + .../io/opencensus/trace/SpanFactoryImplTest.java | 2 + .../trace/config/TraceConfigImplTest.java | 2 +- 28 files changed, 728 insertions(+), 628 deletions(-) delete mode 100644 api/src/main/java/io/opencensus/trace/Sampler.java delete mode 100644 api/src/main/java/io/opencensus/trace/Samplers.java delete mode 100644 api/src/main/java/io/opencensus/trace/StartSpanOptions.java create mode 100644 api/src/main/java/io/opencensus/trace/base/Sampler.java create mode 100644 api/src/main/java/io/opencensus/trace/base/StartSpanOptions.java create mode 100644 api/src/main/java/io/opencensus/trace/samplers/AlwaysSampleSampler.java create mode 100644 api/src/main/java/io/opencensus/trace/samplers/NeverSampleSampler.java create mode 100644 api/src/main/java/io/opencensus/trace/samplers/ProbabilitySampler.java create mode 100644 api/src/main/java/io/opencensus/trace/samplers/Samplers.java delete mode 100644 api/src/test/java/io/opencensus/trace/SamplersTest.java delete mode 100644 api/src/test/java/io/opencensus/trace/StartSpanOptionsTest.java create mode 100644 api/src/test/java/io/opencensus/trace/base/StartSpanOptionsTest.java create mode 100644 api/src/test/java/io/opencensus/trace/samplers/SamplersTest.java diff --git a/api/src/main/java/io/opencensus/trace/Sampler.java b/api/src/main/java/io/opencensus/trace/Sampler.java deleted file mode 100644 index d231f41b..00000000 --- a/api/src/main/java/io/opencensus/trace/Sampler.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.opencensus.trace; - -import io.opencensus.trace.base.SpanId; -import io.opencensus.trace.base.TraceId; -import java.util.List; -import javax.annotation.Nullable; - -/** Sampler is used to make decisions on {@link Span} sampling. */ -public abstract class Sampler { - /** - * Called during {@link Span} creation to make a sampling decision. - * - * @param parentContext The parent {@code Span} {@link SpanContext}. May be {@code null} if this - * is a root span. - * @param remoteParent true if the parentContext is remote. - * @param traceId The {@link TraceId} for the new {@code Span}. This will be identical to that in - * the parentContext, unless this is a root span. - * @param spanId The span ID for the new {@code Span}. - * @param name The name of the new {@code Span}. - * @param parentLinks The parentLinks associated with the new {@code Span}. - * @return {@code true} if the {@code Span} is sampled. - */ - protected abstract boolean shouldSample( - @Nullable SpanContext parentContext, - boolean remoteParent, - TraceId traceId, - SpanId spanId, - String name, - List parentLinks); -} diff --git a/api/src/main/java/io/opencensus/trace/Samplers.java b/api/src/main/java/io/opencensus/trace/Samplers.java deleted file mode 100644 index 8d7908ec..00000000 --- a/api/src/main/java/io/opencensus/trace/Samplers.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.opencensus.trace; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.auto.value.AutoValue; -import io.opencensus.trace.base.SpanId; -import io.opencensus.trace.base.TraceId; -import java.util.List; -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; - -/** Static class to access a set of pre-defined {@link Sampler Samplers}. */ -public final class Samplers { - private static final Sampler ALWAYS_SAMPLE = new AlwaysSampleSampler(); - private static final Sampler NEVER_SAMPLE = new NeverSampleSampler(); - - // No instance of this class. - private Samplers() {} - - /** - * Returns a {@link Sampler} that always makes a "yes" decision on {@link Span} sampling. - * - * @return a {@code Sampler} that always makes a "yes" decision on {@code Span} sampling. - */ - public static Sampler alwaysSample() { - return ALWAYS_SAMPLE; - } - - /** - * Returns a {@link Sampler} that always makes a "no" decision on {@link Span} sampling. - * - * @return a {@code Sampler} that always makes a "no" decision on {@code Span} sampling. - */ - public static Sampler neverSample() { - return NEVER_SAMPLE; - } - - /** - * Returns a {@link Sampler} that makes a "yes" decision with a given probability. - * - * @param probability The desired probability of sampling. Must be within [0.0, 1.0]. - * @return a {@code Sampler} that makes a "yes" decision with a given probability. - * @throws IllegalArgumentException if {@code probability} is out of range - */ - public static Sampler probabilitySampler(double probability) { - return ProbabilitySampler.create(probability); - } - - @Immutable - private static final class AlwaysSampleSampler extends Sampler { - private AlwaysSampleSampler() {} - - // Returns always makes a "yes" decision on {@link Span} sampling. - @Override - protected boolean shouldSample( - @Nullable SpanContext parentContext, - boolean remoteParent, - TraceId traceId, - SpanId spanId, - String name, - List parentLinks) { - return true; - } - - @Override - public String toString() { - return "AlwaysSampleSampler"; - } - } - - @Immutable - private static final class NeverSampleSampler extends Sampler { - private NeverSampleSampler() {} - - // Returns always makes a "no" decision on {@link Span} sampling. - @Override - protected boolean shouldSample( - @Nullable SpanContext parentContext, - boolean remoteParent, - TraceId traceId, - SpanId spanId, - String name, - List parentLinks) { - return false; - } - - @Override - public String toString() { - return "NeverSampleSampler"; - } - } - - // We assume the lower 64 bits of the traceId's are randomly distributed around the whole (long) - // range. We convert an incoming probability into an upper bound on that value, such that we can - // just compare the absolute value of the id and the bound to see if we are within the desired - // probability range. Using the low bits of the traceId also ensures that systems that only use - // 64 bit ID's will also work with this sampler. - @AutoValue - @Immutable - abstract static class ProbabilitySampler extends Sampler { - ProbabilitySampler() {} - - abstract double getProbability(); - - abstract long getIdUpperBound(); - - /** - * Returns a new {@link ProbabilitySampler}. The probability of sampling a trace is equal to - * that of the specified probability. - * - * @param probability The desired probability of sampling. Must be within [0.0, 1.0]. - * @return a new {@link ProbabilitySampler}. - * @throws IllegalArgumentException if {@code probability} is out of range - */ - private static ProbabilitySampler create(double probability) { - checkArgument( - probability >= 0.0 && probability <= 1.0, "probability must be in range [0.0, 1.0]"); - long idUpperBound = 0; - // Special case the limits, to avoid any possible issues with lack of precision across - // double/long boundaries. For probability == 0.0, we use Long.MIN_VALUE as this guarantees - // that we will never sample a trace, even in the case where the id == Long.MIN_VALUE, since - // Math.Abs(Long.MIN_VALUE) == Long.MIN_VALUE. - if (probability == 0.0) { - idUpperBound = Long.MIN_VALUE; - } else if (probability == 1.0) { - idUpperBound = Long.MAX_VALUE; - } else { - idUpperBound = (long) (probability * Long.MAX_VALUE); - } - return new AutoValue_Samplers_ProbabilitySampler(probability, idUpperBound); - } - - @Override - protected final boolean shouldSample( - @Nullable SpanContext parentContext, - boolean remoteParent, - TraceId traceId, - SpanId spanId, - String name, - @Nullable List parentLinks) { - // Always enable sampling if parent was sampled. - if (parentContext != null && parentContext.getTraceOptions().isSampled()) { - return true; - } - // Always sample if we are within probability range. This is true even for child spans (that - // may have had a different sampling decision made) to allow for different sampling policies, - // and dynamic increases to sampling probabilities for debugging purposes. - // Note use of '<' for comparison. This ensures that we never sample for probability == 0.0, - // while allowing for a (very) small chance of *not* sampling if the id == Long.MAX_VALUE. - // This is considered a reasonable tradeoff for the simplicity/performance requirements (this - // code is executed in-line for every Span creation). - return Math.abs(traceId.getLowerLong()) < getIdUpperBound(); - } - } -} diff --git a/api/src/main/java/io/opencensus/trace/Span.java b/api/src/main/java/io/opencensus/trace/Span.java index 0a941287..f1661901 100644 --- a/api/src/main/java/io/opencensus/trace/Span.java +++ b/api/src/main/java/io/opencensus/trace/Span.java @@ -21,6 +21,7 @@ import io.opencensus.trace.base.AttributeValue; import io.opencensus.trace.base.EndSpanOptions; import io.opencensus.trace.base.Link; import io.opencensus.trace.base.NetworkEvent; +import io.opencensus.trace.base.StartSpanOptions; import java.util.Collections; import java.util.EnumSet; import java.util.Map; diff --git a/api/src/main/java/io/opencensus/trace/SpanBuilder.java b/api/src/main/java/io/opencensus/trace/SpanBuilder.java index b3ad8a5f..782248ba 100644 --- a/api/src/main/java/io/opencensus/trace/SpanBuilder.java +++ b/api/src/main/java/io/opencensus/trace/SpanBuilder.java @@ -15,6 +15,8 @@ package io.opencensus.trace; import io.opencensus.common.NonThrowingCloseable; import io.opencensus.trace.base.EndSpanOptions; +import io.opencensus.trace.base.Sampler; +import io.opencensus.trace.base.StartSpanOptions; import io.opencensus.trace.internal.SpanFactory; import java.util.List; import javax.annotation.Nullable; diff --git a/api/src/main/java/io/opencensus/trace/StartSpanOptions.java b/api/src/main/java/io/opencensus/trace/StartSpanOptions.java deleted file mode 100644 index 9b7ccb65..00000000 --- a/api/src/main/java/io/opencensus/trace/StartSpanOptions.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2016, Google Inc. - * 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.opencensus.trace; - -import com.google.auto.value.AutoValue; -import com.google.common.annotations.VisibleForTesting; -import io.opencensus.trace.config.TraceConfig; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; - -/** - * A class that enables overriding the default values used when starting a {@link Span}. Allows - * overriding the {@link Sampler sampler}, the parent links, and option to record all the events - * even if the {@code Span} is not sampled. - */ -@AutoValue -@Immutable -public abstract class StartSpanOptions { - private static final List EMPTY_PARENT_LINKS_LIST = Collections.emptyList(); - - /** The default {@code StartSpanOptions}. */ - @VisibleForTesting static final StartSpanOptions DEFAULT = builder().build(); - - /** - * Returns the {@link Sampler} to be used, or {@code null} if default. - * - * @return the {@code Sampler} to be used, or {@code null} if default. - */ - @Nullable - public abstract Sampler getSampler(); - - /** - * Returns the parent links to be set for the {@link Span}. - * - * @return the parent links to be set for the {@code Span}. - */ - public abstract List getParentLinks(); - - /** - * Returns the record events option, or {@code null} if default. - * - *

See {@link Span.Options#RECORD_EVENTS} for more details. - * - * @return the record events option, or {@code null} if default. - */ - @Nullable - public abstract Boolean getRecordEvents(); - - /** - * Returns a new {@link Builder} with default options. - * - * @return a new {@code Builder} with default options. - */ - public static Builder builder() { - return new AutoValue_StartSpanOptions.Builder().setParentLinks(EMPTY_PARENT_LINKS_LIST); - } - - /** Builder class for {@link StartSpanOptions}. */ - @AutoValue.Builder - public abstract static class Builder { - - /** - * Sets the {@link Sampler} to be used. If {@code null} the default {@code Sampler} from the - * {@link TraceConfig#getActiveTraceParams()} will be used. - * - * @param sampler the {@link Sampler} to be used. - * @return this. - */ - public abstract Builder setSampler(@Nullable Sampler sampler); - - /** - * Sets the parent links to be set for the {@link Span}. - * - * @param parentLinks the parent links to be set for the {@link Span}. - * @return this. - * @throws NullPointerException if {@code parentLinks} is {@code null}. - */ - public abstract Builder setParentLinks(List parentLinks); - - /** - * Sets the record events option. If {@code null} the default value from the {@link - * TraceConfig#getActiveTraceParams()} will be used. - * - *

See {@link Span.Options#RECORD_EVENTS} for more details. - * - * @param recordEvents the record events option. - * @return this. - */ - public abstract Builder setRecordEvents(@Nullable Boolean recordEvents); - - abstract List getParentLinks(); // not public - - abstract StartSpanOptions autoBuild(); // not public - - /** - * Builds and returns a {@code StartSpanOptions} with the desired settings. - * - * @return a {@code StartSpanOptions} with the desired settings. - */ - public StartSpanOptions build() { - setParentLinks(Collections.unmodifiableList(new ArrayList(getParentLinks()))); - return autoBuild(); - } - } - - StartSpanOptions() {} -} diff --git a/api/src/main/java/io/opencensus/trace/Tracer.java b/api/src/main/java/io/opencensus/trace/Tracer.java index 64f0ae49..e6172bed 100644 --- a/api/src/main/java/io/opencensus/trace/Tracer.java +++ b/api/src/main/java/io/opencensus/trace/Tracer.java @@ -16,6 +16,7 @@ package io.opencensus.trace; import static com.google.common.base.Preconditions.checkNotNull; import io.opencensus.common.NonThrowingCloseable; +import io.opencensus.trace.base.StartSpanOptions; import io.opencensus.trace.internal.SpanFactory; import javax.annotation.Nullable; diff --git a/api/src/main/java/io/opencensus/trace/base/Sampler.java b/api/src/main/java/io/opencensus/trace/base/Sampler.java new file mode 100644 index 00000000..94bd2966 --- /dev/null +++ b/api/src/main/java/io/opencensus/trace/base/Sampler.java @@ -0,0 +1,44 @@ +/* + * Copyright 2017, Google Inc. + * 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.opencensus.trace.base; + +import io.opencensus.trace.Span; +import io.opencensus.trace.SpanContext; + +import java.util.List; +import javax.annotation.Nullable; + +/** Sampler is used to make decisions on {@link Span} sampling. */ +public abstract class Sampler { + /** + * Called during {@link Span} creation to make a sampling decision. + * + * @param parentContext The parent {@code Span} {@link SpanContext}. May be {@code null} if this + * is a root span. + * @param remoteParent true if the parentContext is remote. + * @param traceId The {@link TraceId} for the new {@code Span}. This will be identical to that in + * the parentContext, unless this is a root span. + * @param spanId The span ID for the new {@code Span}. + * @param name The name of the new {@code Span}. + * @param parentLinks The parentLinks associated with the new {@code Span}. + * @return {@code true} if the {@code Span} is sampled. + */ + public abstract boolean shouldSample( + @Nullable SpanContext parentContext, + boolean remoteParent, + TraceId traceId, + SpanId spanId, + String name, + List parentLinks); +} diff --git a/api/src/main/java/io/opencensus/trace/base/StartSpanOptions.java b/api/src/main/java/io/opencensus/trace/base/StartSpanOptions.java new file mode 100644 index 00000000..518f1e60 --- /dev/null +++ b/api/src/main/java/io/opencensus/trace/base/StartSpanOptions.java @@ -0,0 +1,122 @@ +/* + * Copyright 2016, Google Inc. + * 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.opencensus.trace.base; + +import com.google.auto.value.AutoValue; +import com.google.common.annotations.VisibleForTesting; +import io.opencensus.trace.Span; +import io.opencensus.trace.config.TraceConfig; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** + * A class that enables overriding the default values used when starting a {@link Span}. Allows + * overriding the {@link Sampler sampler}, the parent links, and option to record all the events + * even if the {@code Span} is not sampled. + */ +@AutoValue +@Immutable +public abstract class StartSpanOptions { + private static final List EMPTY_PARENT_LINKS_LIST = Collections.emptyList(); + + /** The default {@code StartSpanOptions}. */ + @VisibleForTesting public static final StartSpanOptions DEFAULT = builder().build(); + + /** + * Returns the {@link Sampler} to be used, or {@code null} if default. + * + * @return the {@code Sampler} to be used, or {@code null} if default. + */ + @Nullable + public abstract Sampler getSampler(); + + /** + * Returns the parent links to be set for the {@link Span}. + * + * @return the parent links to be set for the {@code Span}. + */ + public abstract List getParentLinks(); + + /** + * Returns the record events option, or {@code null} if default. + * + *

See {@link Span.Options#RECORD_EVENTS} for more details. + * + * @return the record events option, or {@code null} if default. + */ + @Nullable + public abstract Boolean getRecordEvents(); + + /** + * Returns a new {@link Builder} with default options. + * + * @return a new {@code Builder} with default options. + */ + public static Builder builder() { + return new AutoValue_StartSpanOptions.Builder().setParentLinks(EMPTY_PARENT_LINKS_LIST); + } + + /** Builder class for {@link StartSpanOptions}. */ + @AutoValue.Builder + public abstract static class Builder { + + /** + * Sets the {@link Sampler} to be used. If {@code null} the default {@code Sampler} from the + * {@link TraceConfig#getActiveTraceParams()} will be used. + * + * @param sampler the {@link Sampler} to be used. + * @return this. + */ + public abstract Builder setSampler(@Nullable Sampler sampler); + + /** + * Sets the parent links to be set for the {@link Span}. + * + * @param parentLinks the parent links to be set for the {@link Span}. + * @return this. + * @throws NullPointerException if {@code parentLinks} is {@code null}. + */ + public abstract Builder setParentLinks(List parentLinks); + + /** + * Sets the record events option. If {@code null} the default value from the {@link + * TraceConfig#getActiveTraceParams()} will be used. + * + *

See {@link Span.Options#RECORD_EVENTS} for more details. + * + * @param recordEvents the record events option. + * @return this. + */ + public abstract Builder setRecordEvents(@Nullable Boolean recordEvents); + + abstract List getParentLinks(); // not public + + abstract StartSpanOptions autoBuild(); // not public + + /** + * Builds and returns a {@code StartSpanOptions} with the desired settings. + * + * @return a {@code StartSpanOptions} with the desired settings. + */ + public StartSpanOptions build() { + setParentLinks(Collections.unmodifiableList(new ArrayList(getParentLinks()))); + return autoBuild(); + } + } + + StartSpanOptions() {} +} diff --git a/api/src/main/java/io/opencensus/trace/config/TraceParams.java b/api/src/main/java/io/opencensus/trace/config/TraceParams.java index 1f0e093f..74953cfd 100644 --- a/api/src/main/java/io/opencensus/trace/config/TraceParams.java +++ b/api/src/main/java/io/opencensus/trace/config/TraceParams.java @@ -16,13 +16,13 @@ package io.opencensus.trace.config; import static com.google.common.base.Preconditions.checkArgument; import com.google.auto.value.AutoValue; -import io.opencensus.trace.Sampler; -import io.opencensus.trace.Samplers; import io.opencensus.trace.Span; -import io.opencensus.trace.StartSpanOptions; import io.opencensus.trace.base.Annotation; import io.opencensus.trace.base.Link; import io.opencensus.trace.base.NetworkEvent; +import io.opencensus.trace.base.Sampler; +import io.opencensus.trace.base.StartSpanOptions; +import io.opencensus.trace.samplers.Samplers; import javax.annotation.concurrent.Immutable; /** Class that holds global trace parameters. */ @@ -109,8 +109,8 @@ public abstract class TraceParams { /** * Sets the global default max number of attributes per {@link Span}. * - * @param maxNumberOfAttributes the global default max number of attributes per {@link Span}. - * It must be positive otherwise {@link #build()} will throw an exception. + * @param maxNumberOfAttributes the global default max number of attributes per {@link Span}. It + * must be positive otherwise {@link #build()} will throw an exception. * @return this. */ public abstract Builder setMaxNumberOfAttributes(int maxNumberOfAttributes); @@ -118,9 +118,8 @@ public abstract class TraceParams { /** * Sets the global default max number of {@link Annotation} events per {@link Span}. * - * @param maxNumberOfAnnotations the global default max number of {@link Annotation} events - * per {@link Span}. It must be positive otherwise {@link #build()} will throw an - * exception. + * @param maxNumberOfAnnotations the global default max number of {@link Annotation} events per + * {@link Span}. It must be positive otherwise {@link #build()} will throw an exception. * @return this. */ public abstract Builder setMaxNumberOfAnnotations(int maxNumberOfAnnotations); @@ -128,9 +127,8 @@ public abstract class TraceParams { /** * Sets the global default max number of {@link NetworkEvent} events per {@link Span}. * - * @param maxNumberOfNetworkEvents the global default max number of {@link NetworkEvent} - * events per {@link Span}. It must be positive otherwise {@link #build()} will throw an - * exception. + * @param maxNumberOfNetworkEvents the global default max number of {@link NetworkEvent} events + * per {@link Span}. It must be positive otherwise {@link #build()} will throw an exception. * @return this. */ public abstract Builder setMaxNumberOfNetworkEvents(int maxNumberOfNetworkEvents); diff --git a/api/src/main/java/io/opencensus/trace/internal/SpanFactory.java b/api/src/main/java/io/opencensus/trace/internal/SpanFactory.java index b93ec865..a0306152 100644 --- a/api/src/main/java/io/opencensus/trace/internal/SpanFactory.java +++ b/api/src/main/java/io/opencensus/trace/internal/SpanFactory.java @@ -15,7 +15,7 @@ package io.opencensus.trace.internal; import io.opencensus.trace.Span; import io.opencensus.trace.SpanContext; -import io.opencensus.trace.StartSpanOptions; +import io.opencensus.trace.base.StartSpanOptions; import javax.annotation.Nullable; /** Factory class to create and start a {@link Span}. */ diff --git a/api/src/main/java/io/opencensus/trace/samplers/AlwaysSampleSampler.java b/api/src/main/java/io/opencensus/trace/samplers/AlwaysSampleSampler.java new file mode 100644 index 00000000..f3daed4b --- /dev/null +++ b/api/src/main/java/io/opencensus/trace/samplers/AlwaysSampleSampler.java @@ -0,0 +1,50 @@ +/* + * Copyright 2017, Google Inc. + * 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.opencensus.trace.samplers; + +import io.opencensus.trace.Span; +import io.opencensus.trace.SpanContext; +import io.opencensus.trace.base.Sampler; +import io.opencensus.trace.base.SpanId; +import io.opencensus.trace.base.TraceId; +import java.util.List; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** + * Sampler that always makes a "yes" decision on {@link Span} sampling. + */ +@Immutable +final class AlwaysSampleSampler extends Sampler { + + AlwaysSampleSampler() { + } + + // Returns always makes a "yes" decision on {@link Span} sampling. + @Override + public boolean shouldSample( + @Nullable SpanContext parentContext, + boolean remoteParent, + TraceId traceId, + SpanId spanId, + String name, + List parentLinks) { + return true; + } + + @Override + public String toString() { + return "AlwaysSampleSampler"; + } +} diff --git a/api/src/main/java/io/opencensus/trace/samplers/NeverSampleSampler.java b/api/src/main/java/io/opencensus/trace/samplers/NeverSampleSampler.java new file mode 100644 index 00000000..bca64df4 --- /dev/null +++ b/api/src/main/java/io/opencensus/trace/samplers/NeverSampleSampler.java @@ -0,0 +1,50 @@ +/* + * Copyright 2017, Google Inc. + * 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.opencensus.trace.samplers; + +import io.opencensus.trace.Span; +import io.opencensus.trace.SpanContext; +import io.opencensus.trace.base.Sampler; +import io.opencensus.trace.base.SpanId; +import io.opencensus.trace.base.TraceId; +import java.util.List; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** + * Sampler that always makes a "no" decision on {@link Span} sampling. + */ +@Immutable +final class NeverSampleSampler extends Sampler { + + NeverSampleSampler() { + } + + // Returns always makes a "no" decision on {@link Span} sampling. + @Override + public boolean shouldSample( + @Nullable SpanContext parentContext, + boolean remoteParent, + TraceId traceId, + SpanId spanId, + String name, + List parentLinks) { + return false; + } + + @Override + public String toString() { + return "NeverSampleSampler"; + } +} diff --git a/api/src/main/java/io/opencensus/trace/samplers/ProbabilitySampler.java b/api/src/main/java/io/opencensus/trace/samplers/ProbabilitySampler.java new file mode 100644 index 00000000..c5c9c002 --- /dev/null +++ b/api/src/main/java/io/opencensus/trace/samplers/ProbabilitySampler.java @@ -0,0 +1,93 @@ +/* + * Copyright 2017, Google Inc. + * 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.opencensus.trace.samplers; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.auto.value.AutoValue; +import io.opencensus.trace.Span; +import io.opencensus.trace.SpanContext; +import io.opencensus.trace.base.Sampler; +import io.opencensus.trace.base.SpanId; +import io.opencensus.trace.base.TraceId; +import java.util.List; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** + * We assume the lower 64 bits of the traceId's are randomly distributed around the whole (long) + * range. We convert an incoming probability into an upper bound on that value, such that we can + * just compare the absolute value of the id and the bound to see if we are within the desired + * probability range. Using the low bits of the traceId also ensures that systems that only use + * 64 bit ID's will also work with this sampler. + */ +@AutoValue +@Immutable +abstract class ProbabilitySampler extends Sampler { + + ProbabilitySampler() { + } + + abstract double getProbability(); + + abstract long getIdUpperBound(); + + /** + * Returns a new {@link ProbabilitySampler}. The probability of sampling a trace is equal to + * that of the specified probability. + * + * @param probability The desired probability of sampling. Must be within [0.0, 1.0]. + * @return a new {@link ProbabilitySampler}. + * @throws IllegalArgumentException if {@code probability} is out of range + */ + static ProbabilitySampler create(double probability) { + checkArgument( + probability >= 0.0 && probability <= 1.0, "probability must be in range [0.0, 1.0]"); + long idUpperBound = 0; + // Special case the limits, to avoid any possible issues with lack of precision across + // double/long boundaries. For probability == 0.0, we use Long.MIN_VALUE as this guarantees + // that we will never sample a trace, even in the case where the id == Long.MIN_VALUE, since + // Math.Abs(Long.MIN_VALUE) == Long.MIN_VALUE. + if (probability == 0.0) { + idUpperBound = Long.MIN_VALUE; + } else if (probability == 1.0) { + idUpperBound = Long.MAX_VALUE; + } else { + idUpperBound = (long) (probability * Long.MAX_VALUE); + } + return new AutoValue_ProbabilitySampler(probability, idUpperBound); + } + + @Override + public final boolean shouldSample( + @Nullable SpanContext parentContext, + boolean remoteParent, + TraceId traceId, + SpanId spanId, + String name, + @Nullable List parentLinks) { + // Always enable sampling if parent was sampled. + if (parentContext != null && parentContext.getTraceOptions().isSampled()) { + return true; + } + // Always sample if we are within probability range. This is true even for child spans (that + // may have had a different sampling decision made) to allow for different sampling policies, + // and dynamic increases to sampling probabilities for debugging purposes. + // Note use of '<' for comparison. This ensures that we never sample for probability == 0.0, + // while allowing for a (very) small chance of *not* sampling if the id == Long.MAX_VALUE. + // This is considered a reasonable tradeoff for the simplicity/performance requirements (this + // code is executed in-line for every Span creation). + return Math.abs(traceId.getLowerLong()) < getIdUpperBound(); + } +} diff --git a/api/src/main/java/io/opencensus/trace/samplers/Samplers.java b/api/src/main/java/io/opencensus/trace/samplers/Samplers.java new file mode 100644 index 00000000..40d72931 --- /dev/null +++ b/api/src/main/java/io/opencensus/trace/samplers/Samplers.java @@ -0,0 +1,55 @@ +/* + * Copyright 2017, Google Inc. + * 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.opencensus.trace.samplers; + +import io.opencensus.trace.Span; +import io.opencensus.trace.base.Sampler; + +/** Static class to access a set of pre-defined {@link Sampler Samplers}. */ +public final class Samplers { + private static final Sampler ALWAYS_SAMPLE = new AlwaysSampleSampler(); + private static final Sampler NEVER_SAMPLE = new NeverSampleSampler(); + + // No instance of this class. + private Samplers() {} + + /** + * Returns a {@link Sampler} that always makes a "yes" decision on {@link Span} sampling. + * + * @return a {@code Sampler} that always makes a "yes" decision on {@code Span} sampling. + */ + public static Sampler alwaysSample() { + return ALWAYS_SAMPLE; + } + + /** + * Returns a {@link Sampler} that always makes a "no" decision on {@link Span} sampling. + * + * @return a {@code Sampler} that always makes a "no" decision on {@code Span} sampling. + */ + public static Sampler neverSample() { + return NEVER_SAMPLE; + } + + /** + * Returns a {@link Sampler} that makes a "yes" decision with a given probability. + * + * @param probability The desired probability of sampling. Must be within [0.0, 1.0]. + * @return a {@code Sampler} that makes a "yes" decision with a given probability. + * @throws IllegalArgumentException if {@code probability} is out of range + */ + public static Sampler probabilitySampler(double probability) { + return ProbabilitySampler.create(probability); + } +} diff --git a/api/src/test/java/io/opencensus/trace/SamplersTest.java b/api/src/test/java/io/opencensus/trace/SamplersTest.java deleted file mode 100644 index 52351f2c..00000000 --- a/api/src/test/java/io/opencensus/trace/SamplersTest.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.opencensus.trace; - -import static com.google.common.truth.Truth.assertThat; - -import io.opencensus.trace.base.SpanId; -import io.opencensus.trace.base.TraceId; -import io.opencensus.trace.base.TraceOptions; -import java.util.Collections; -import java.util.Random; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Unit tests for {@link Samplers}. */ -@RunWith(JUnit4.class) -public class SamplersTest { - private final Random random = new Random(1234); - private final TraceId traceId = TraceId.generateRandomId(random); - private final SpanId parentSpanId = SpanId.generateRandomId(random); - private final SpanId spanId = SpanId.generateRandomId(random); - private final SpanContext sampledSpanContext = - SpanContext.create(traceId, parentSpanId, TraceOptions.builder().setIsSampled().build()); - private final SpanContext notSampledSpanContext = - SpanContext.create(traceId, parentSpanId, TraceOptions.DEFAULT); - - @Test - public void alwaysSampleSampler_AlwaysReturnTrue() { - // Sampled parent. - assertThat( - Samplers.alwaysSample() - .shouldSample( - sampledSpanContext, - false, - traceId, - spanId, - "Another name", - Collections.emptyList())) - .isTrue(); - // Not sampled parent. - assertThat( - Samplers.alwaysSample() - .shouldSample( - notSampledSpanContext, - false, - traceId, - spanId, - "Yet another name", - Collections.emptyList())) - .isTrue(); - } - - @Test - public void alwaysSampleSampler_ToString() { - assertThat(Samplers.alwaysSample().toString()).isEqualTo("AlwaysSampleSampler"); - } - - @Test - public void neverSampleSampler_AlwaysReturnFalse() { - // Sampled parent. - assertThat( - Samplers.neverSample() - .shouldSample( - sampledSpanContext, - false, - traceId, - spanId, - "bar", - Collections.emptyList())) - .isFalse(); - // Not sampled parent. - assertThat( - Samplers.neverSample() - .shouldSample( - notSampledSpanContext, - false, - traceId, - spanId, - "quux", - Collections.emptyList())) - .isFalse(); - } - - @Test - public void neverSampleSampler_ToString() { - assertThat(Samplers.neverSample().toString()).isEqualTo("NeverSampleSampler"); - } - - @Test(expected = IllegalArgumentException.class) - public void probabilitySampler_outOfRangeHighProbability() { - Samplers.probabilitySampler(1.01); - } - - @Test(expected = IllegalArgumentException.class) - public void probabilitySampler_outOfRangeLowProbability() { - Samplers.probabilitySampler(-0.00001); - } - - private final void probabilitySampler_AlwaysReturnTrueForSampled(Sampler sampler) { - final int numSamples = 100; // Number of traces for which to generate sampling decisions. - for (int i = 0; i < numSamples; i++) { - assertThat( - sampler.shouldSample( - sampledSpanContext, - false, - TraceId.generateRandomId(random), - spanId, - "bar", - Collections.emptyList())) - .isTrue(); - } - } - - private final void probabilitySampler_SamplesWithProbabilityForUnsampled( - Sampler sampler, double probability) { - final int numSamples = 1000; // Number of traces for which to generate sampling decisions. - int count = 0; // Count of spans with sampling enabled - for (int i = 0; i < numSamples; i++) { - if (sampler.shouldSample( - notSampledSpanContext, - false, - TraceId.generateRandomId(random), - spanId, - "bar", - Collections.emptyList())) { - count++; - } - } - double proportionSampled = (double) count / numSamples; - // Allow for a large amount of slop (+/- 10%) in number of sampled traces, to avoid flakiness. - assertThat(proportionSampled < probability + 0.1 && proportionSampled > probability - 0.1) - .isTrue(); - } - - @Test - public void probabilitySamper_SamplesWithProbability() { - final Sampler neverSample = Samplers.probabilitySampler(0.0); - probabilitySampler_AlwaysReturnTrueForSampled(neverSample); - probabilitySampler_SamplesWithProbabilityForUnsampled(neverSample, 0.0); - final Sampler alwaysSample = Samplers.probabilitySampler(1.0); - probabilitySampler_AlwaysReturnTrueForSampled(alwaysSample); - probabilitySampler_SamplesWithProbabilityForUnsampled(alwaysSample, 1.0); - final Sampler fiftyPercentSample = Samplers.probabilitySampler(0.5); - probabilitySampler_AlwaysReturnTrueForSampled(fiftyPercentSample); - probabilitySampler_SamplesWithProbabilityForUnsampled(fiftyPercentSample, 0.5); - final Sampler twentyPercentSample = Samplers.probabilitySampler(0.2); - probabilitySampler_AlwaysReturnTrueForSampled(twentyPercentSample); - probabilitySampler_SamplesWithProbabilityForUnsampled(twentyPercentSample, 0.2); - final Sampler twoThirdsSample = Samplers.probabilitySampler(2.0 / 3.0); - probabilitySampler_AlwaysReturnTrueForSampled(twoThirdsSample); - probabilitySampler_SamplesWithProbabilityForUnsampled(twoThirdsSample, 2.0 / 3.0); - } - - @Test - public void probabilitySampler_ToString() { - assertThat((Samplers.probabilitySampler(0.5)).toString()).contains("0.5"); - } -} diff --git a/api/src/test/java/io/opencensus/trace/SpanBuilderTest.java b/api/src/test/java/io/opencensus/trace/SpanBuilderTest.java index 469fe9bb..10eccbe6 100644 --- a/api/src/test/java/io/opencensus/trace/SpanBuilderTest.java +++ b/api/src/test/java/io/opencensus/trace/SpanBuilderTest.java @@ -23,9 +23,11 @@ import static org.mockito.Mockito.when; import io.opencensus.common.NonThrowingCloseable; import io.opencensus.trace.base.EndSpanOptions; import io.opencensus.trace.base.SpanId; +import io.opencensus.trace.base.StartSpanOptions; import io.opencensus.trace.base.TraceId; import io.opencensus.trace.base.TraceOptions; import io.opencensus.trace.internal.SpanFactory; +import io.opencensus.trace.samplers.Samplers; import java.util.Arrays; import java.util.List; import java.util.Random; diff --git a/api/src/test/java/io/opencensus/trace/StartSpanOptionsTest.java b/api/src/test/java/io/opencensus/trace/StartSpanOptionsTest.java deleted file mode 100644 index c37324ab..00000000 --- a/api/src/test/java/io/opencensus/trace/StartSpanOptionsTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2017, Google Inc. - * 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.opencensus.trace; - -import static com.google.common.truth.Truth.assertThat; - -import com.google.common.testing.EqualsTester; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Unit tests for {@link StartSpanOptions}. */ -@RunWith(JUnit4.class) -public class StartSpanOptionsTest { - private final List singleParentList = Arrays.asList(BlankSpan.INSTANCE); - - @Test - public void defaultOptions() { - StartSpanOptions defaultOptions = StartSpanOptions.builder().build(); - assertThat(defaultOptions.getSampler()).isNull(); - assertThat(defaultOptions.getParentLinks().isEmpty()).isTrue(); - assertThat(defaultOptions.getRecordEvents()).isNull(); - } - - @Test - public void setSampler() { - StartSpanOptions options = - StartSpanOptions.builder().setSampler(Samplers.neverSample()).build(); - assertThat(options.getSampler()).isEqualTo(Samplers.neverSample()); - assertThat(options.getParentLinks().isEmpty()).isTrue(); - assertThat(options.getRecordEvents()).isNull(); - } - - @Test - public void setParentLinks() { - StartSpanOptions options = StartSpanOptions.builder().setParentLinks(singleParentList).build(); - assertThat(options.getSampler()).isNull(); - assertThat(options.getParentLinks()).isEqualTo(singleParentList); - assertThat(options.getRecordEvents()).isNull(); - } - - @Test - public void setParentLinks_EmptyList() { - StartSpanOptions options = - StartSpanOptions.builder().setParentLinks(new LinkedList()).build(); - assertThat(options.getSampler()).isNull(); - assertThat(options.getParentLinks().size()).isEqualTo(0); - assertThat(options.getRecordEvents()).isNull(); - } - - @Test - public void setParentLinks_MultipleParents() { - StartSpanOptions options = - StartSpanOptions.builder() - .setParentLinks(Arrays.asList(BlankSpan.INSTANCE, BlankSpan.INSTANCE)) - .build(); - assertThat(options.getSampler()).isNull(); - assertThat(options.getParentLinks().size()).isEqualTo(2); - assertThat(options.getRecordEvents()).isNull(); - } - - @Test - public void setRecordEvents() { - StartSpanOptions options = StartSpanOptions.builder().setRecordEvents(true).build(); - assertThat(options.getSampler()).isNull(); - assertThat(options.getParentLinks().isEmpty()).isTrue(); - assertThat(options.getRecordEvents()).isTrue(); - } - - @Test - public void setAllProperties() { - StartSpanOptions options = - StartSpanOptions.builder() - .setSampler(Samplers.alwaysSample()) - .setRecordEvents(true) - .setParentLinks(singleParentList) - .build(); - assertThat(options.getSampler()).isEqualTo(Samplers.alwaysSample()); - assertThat(options.getParentLinks()).isEqualTo(singleParentList); - assertThat(options.getRecordEvents()).isTrue(); - } - - @Test - public void startSpanOptions_EqualsAndHashCode() { - EqualsTester tester = new EqualsTester(); - StartSpanOptions optionsWithAlwaysSampler1 = - StartSpanOptions.builder().setSampler(Samplers.alwaysSample()).build(); - StartSpanOptions optionsWithAlwaysSampler2 = - StartSpanOptions.builder().setSampler(Samplers.alwaysSample()).build(); - tester.addEqualityGroup(optionsWithAlwaysSampler1, optionsWithAlwaysSampler2); - StartSpanOptions optionsWithNeverSampler = - StartSpanOptions.builder().setSampler(Samplers.neverSample()).build(); - tester.addEqualityGroup(optionsWithNeverSampler); - tester.addEqualityGroup(StartSpanOptions.DEFAULT); - tester.testEquals(); - } -} diff --git a/api/src/test/java/io/opencensus/trace/TracerTest.java b/api/src/test/java/io/opencensus/trace/TracerTest.java index 51b86b63..7339eae2 100644 --- a/api/src/test/java/io/opencensus/trace/TracerTest.java +++ b/api/src/test/java/io/opencensus/trace/TracerTest.java @@ -24,6 +24,7 @@ import io.grpc.Context; import io.opencensus.common.NonThrowingCloseable; import io.opencensus.trace.base.EndSpanOptions; import io.opencensus.trace.base.SpanId; +import io.opencensus.trace.base.StartSpanOptions; import io.opencensus.trace.base.TraceId; import io.opencensus.trace.base.TraceOptions; import io.opencensus.trace.internal.SpanFactory; diff --git a/api/src/test/java/io/opencensus/trace/base/StartSpanOptionsTest.java b/api/src/test/java/io/opencensus/trace/base/StartSpanOptionsTest.java new file mode 100644 index 00000000..1a983b73 --- /dev/null +++ b/api/src/test/java/io/opencensus/trace/base/StartSpanOptionsTest.java @@ -0,0 +1,114 @@ +/* + * Copyright 2017, Google Inc. + * 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.opencensus.trace.base; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.testing.EqualsTester; +import io.opencensus.trace.BlankSpan; +import io.opencensus.trace.Span; +import io.opencensus.trace.samplers.Samplers; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link StartSpanOptions}. */ +@RunWith(JUnit4.class) +public class StartSpanOptionsTest { + private final List singleParentList = Arrays.asList(BlankSpan.INSTANCE); + + @Test + public void defaultOptions() { + StartSpanOptions defaultOptions = StartSpanOptions.builder().build(); + assertThat(defaultOptions.getSampler()).isNull(); + assertThat(defaultOptions.getParentLinks().isEmpty()).isTrue(); + assertThat(defaultOptions.getRecordEvents()).isNull(); + } + + @Test + public void setSampler() { + StartSpanOptions options = + StartSpanOptions.builder().setSampler(Samplers.neverSample()).build(); + assertThat(options.getSampler()).isEqualTo(Samplers.neverSample()); + assertThat(options.getParentLinks().isEmpty()).isTrue(); + assertThat(options.getRecordEvents()).isNull(); + } + + @Test + public void setParentLinks() { + StartSpanOptions options = StartSpanOptions.builder().setParentLinks(singleParentList).build(); + assertThat(options.getSampler()).isNull(); + assertThat(options.getParentLinks()).isEqualTo(singleParentList); + assertThat(options.getRecordEvents()).isNull(); + } + + @Test + public void setParentLinks_EmptyList() { + StartSpanOptions options = + StartSpanOptions.builder().setParentLinks(new LinkedList()).build(); + assertThat(options.getSampler()).isNull(); + assertThat(options.getParentLinks().size()).isEqualTo(0); + assertThat(options.getRecordEvents()).isNull(); + } + + @Test + public void setParentLinks_MultipleParents() { + StartSpanOptions options = + StartSpanOptions.builder() + .setParentLinks(Arrays.asList(BlankSpan.INSTANCE, BlankSpan.INSTANCE)) + .build(); + assertThat(options.getSampler()).isNull(); + assertThat(options.getParentLinks().size()).isEqualTo(2); + assertThat(options.getRecordEvents()).isNull(); + } + + @Test + public void setRecordEvents() { + StartSpanOptions options = StartSpanOptions.builder().setRecordEvents(true).build(); + assertThat(options.getSampler()).isNull(); + assertThat(options.getParentLinks().isEmpty()).isTrue(); + assertThat(options.getRecordEvents()).isTrue(); + } + + @Test + public void setAllProperties() { + StartSpanOptions options = + StartSpanOptions.builder() + .setSampler(Samplers.alwaysSample()) + .setRecordEvents(true) + .setParentLinks(singleParentList) + .build(); + assertThat(options.getSampler()).isEqualTo(Samplers.alwaysSample()); + assertThat(options.getParentLinks()).isEqualTo(singleParentList); + assertThat(options.getRecordEvents()).isTrue(); + } + + @Test + public void startSpanOptions_EqualsAndHashCode() { + EqualsTester tester = new EqualsTester(); + StartSpanOptions optionsWithAlwaysSampler1 = + StartSpanOptions.builder().setSampler(Samplers.alwaysSample()).build(); + StartSpanOptions optionsWithAlwaysSampler2 = + StartSpanOptions.builder().setSampler(Samplers.alwaysSample()).build(); + tester.addEqualityGroup(optionsWithAlwaysSampler1, optionsWithAlwaysSampler2); + StartSpanOptions optionsWithNeverSampler = + StartSpanOptions.builder().setSampler(Samplers.neverSample()).build(); + tester.addEqualityGroup(optionsWithNeverSampler); + tester.addEqualityGroup(StartSpanOptions.DEFAULT); + tester.testEquals(); + } +} diff --git a/api/src/test/java/io/opencensus/trace/config/TraceConfigTest.java b/api/src/test/java/io/opencensus/trace/config/TraceConfigTest.java index 2bf9356d..cbb6092c 100644 --- a/api/src/test/java/io/opencensus/trace/config/TraceConfigTest.java +++ b/api/src/test/java/io/opencensus/trace/config/TraceConfigTest.java @@ -15,7 +15,7 @@ package io.opencensus.trace.config; import static com.google.common.truth.Truth.assertThat; -import io.opencensus.trace.Samplers; +import io.opencensus.trace.samplers.Samplers; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; diff --git a/api/src/test/java/io/opencensus/trace/config/TraceParamsTest.java b/api/src/test/java/io/opencensus/trace/config/TraceParamsTest.java index 76d828a9..165f0211 100644 --- a/api/src/test/java/io/opencensus/trace/config/TraceParamsTest.java +++ b/api/src/test/java/io/opencensus/trace/config/TraceParamsTest.java @@ -15,7 +15,7 @@ package io.opencensus.trace.config; import static com.google.common.truth.Truth.assertThat; -import io.opencensus.trace.Samplers; +import io.opencensus.trace.samplers.Samplers; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; diff --git a/api/src/test/java/io/opencensus/trace/samplers/SamplersTest.java b/api/src/test/java/io/opencensus/trace/samplers/SamplersTest.java new file mode 100644 index 00000000..5e996835 --- /dev/null +++ b/api/src/test/java/io/opencensus/trace/samplers/SamplersTest.java @@ -0,0 +1,173 @@ +/* + * Copyright 2017, Google Inc. + * 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.opencensus.trace.samplers; + +import static com.google.common.truth.Truth.assertThat; + +import io.opencensus.trace.Span; +import io.opencensus.trace.SpanContext; +import io.opencensus.trace.base.Sampler; +import io.opencensus.trace.base.SpanId; +import io.opencensus.trace.base.TraceId; +import io.opencensus.trace.base.TraceOptions; +import java.util.Collections; +import java.util.Random; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link Samplers}. */ +@RunWith(JUnit4.class) +public class SamplersTest { + private final Random random = new Random(1234); + private final TraceId traceId = TraceId.generateRandomId(random); + private final SpanId parentSpanId = SpanId.generateRandomId(random); + private final SpanId spanId = SpanId.generateRandomId(random); + private final SpanContext sampledSpanContext = + SpanContext.create(traceId, parentSpanId, TraceOptions.builder().setIsSampled().build()); + private final SpanContext notSampledSpanContext = + SpanContext.create(traceId, parentSpanId, TraceOptions.DEFAULT); + + @Test + public void alwaysSampleSampler_AlwaysReturnTrue() { + // Sampled parent. + assertThat( + Samplers.alwaysSample() + .shouldSample( + sampledSpanContext, + false, + traceId, + spanId, + "Another name", + Collections.emptyList())) + .isTrue(); + // Not sampled parent. + assertThat( + Samplers.alwaysSample() + .shouldSample( + notSampledSpanContext, + false, + traceId, + spanId, + "Yet another name", + Collections.emptyList())) + .isTrue(); + } + + @Test + public void alwaysSampleSampler_ToString() { + assertThat(Samplers.alwaysSample().toString()).isEqualTo("AlwaysSampleSampler"); + } + + @Test + public void neverSampleSampler_AlwaysReturnFalse() { + // Sampled parent. + assertThat( + Samplers.neverSample() + .shouldSample( + sampledSpanContext, + false, + traceId, + spanId, + "bar", + Collections.emptyList())) + .isFalse(); + // Not sampled parent. + assertThat( + Samplers.neverSample() + .shouldSample( + notSampledSpanContext, + false, + traceId, + spanId, + "quux", + Collections.emptyList())) + .isFalse(); + } + + @Test + public void neverSampleSampler_ToString() { + assertThat(Samplers.neverSample().toString()).isEqualTo("NeverSampleSampler"); + } + + @Test(expected = IllegalArgumentException.class) + public void probabilitySampler_outOfRangeHighProbability() { + Samplers.probabilitySampler(1.01); + } + + @Test(expected = IllegalArgumentException.class) + public void probabilitySampler_outOfRangeLowProbability() { + Samplers.probabilitySampler(-0.00001); + } + + private final void probabilitySampler_AlwaysReturnTrueForSampled(Sampler sampler) { + final int numSamples = 100; // Number of traces for which to generate sampling decisions. + for (int i = 0; i < numSamples; i++) { + assertThat( + sampler.shouldSample( + sampledSpanContext, + false, + TraceId.generateRandomId(random), + spanId, + "bar", + Collections.emptyList())) + .isTrue(); + } + } + + private final void probabilitySampler_SamplesWithProbabilityForUnsampled( + Sampler sampler, double probability) { + final int numSamples = 1000; // Number of traces for which to generate sampling decisions. + int count = 0; // Count of spans with sampling enabled + for (int i = 0; i < numSamples; i++) { + if (sampler.shouldSample( + notSampledSpanContext, + false, + TraceId.generateRandomId(random), + spanId, + "bar", + Collections.emptyList())) { + count++; + } + } + double proportionSampled = (double) count / numSamples; + // Allow for a large amount of slop (+/- 10%) in number of sampled traces, to avoid flakiness. + assertThat(proportionSampled < probability + 0.1 && proportionSampled > probability - 0.1) + .isTrue(); + } + + @Test + public void probabilitySamper_SamplesWithProbability() { + final Sampler neverSample = Samplers.probabilitySampler(0.0); + probabilitySampler_AlwaysReturnTrueForSampled(neverSample); + probabilitySampler_SamplesWithProbabilityForUnsampled(neverSample, 0.0); + final Sampler alwaysSample = Samplers.probabilitySampler(1.0); + probabilitySampler_AlwaysReturnTrueForSampled(alwaysSample); + probabilitySampler_SamplesWithProbabilityForUnsampled(alwaysSample, 1.0); + final Sampler fiftyPercentSample = Samplers.probabilitySampler(0.5); + probabilitySampler_AlwaysReturnTrueForSampled(fiftyPercentSample); + probabilitySampler_SamplesWithProbabilityForUnsampled(fiftyPercentSample, 0.5); + final Sampler twentyPercentSample = Samplers.probabilitySampler(0.2); + probabilitySampler_AlwaysReturnTrueForSampled(twentyPercentSample); + probabilitySampler_SamplesWithProbabilityForUnsampled(twentyPercentSample, 0.2); + final Sampler twoThirdsSample = Samplers.probabilitySampler(2.0 / 3.0); + probabilitySampler_AlwaysReturnTrueForSampled(twoThirdsSample); + probabilitySampler_SamplesWithProbabilityForUnsampled(twoThirdsSample, 2.0 / 3.0); + } + + @Test + public void probabilitySampler_ToString() { + assertThat((Samplers.probabilitySampler(0.5)).toString()).contains("0.5"); + } +} diff --git a/benchmarks/src/jmh/java/io/opencensus/trace/RecordTraceEventsNonSampledSpanBenchmark.java b/benchmarks/src/jmh/java/io/opencensus/trace/RecordTraceEventsNonSampledSpanBenchmark.java index 13657e1b..3013e764 100644 --- a/benchmarks/src/jmh/java/io/opencensus/trace/RecordTraceEventsNonSampledSpanBenchmark.java +++ b/benchmarks/src/jmh/java/io/opencensus/trace/RecordTraceEventsNonSampledSpanBenchmark.java @@ -16,6 +16,7 @@ package io.opencensus.trace; import io.opencensus.trace.base.AttributeValue; import io.opencensus.trace.base.Link; import io.opencensus.trace.base.NetworkEvent; +import io.opencensus.trace.samplers.Samplers; import java.util.HashMap; import java.util.concurrent.TimeUnit; import org.openjdk.jmh.annotations.Benchmark; diff --git a/benchmarks/src/jmh/java/io/opencensus/trace/RecordTraceEventsSampledSpanBenchmark.java b/benchmarks/src/jmh/java/io/opencensus/trace/RecordTraceEventsSampledSpanBenchmark.java index 3c2a9d17..ccfbc775 100644 --- a/benchmarks/src/jmh/java/io/opencensus/trace/RecordTraceEventsSampledSpanBenchmark.java +++ b/benchmarks/src/jmh/java/io/opencensus/trace/RecordTraceEventsSampledSpanBenchmark.java @@ -16,6 +16,7 @@ package io.opencensus.trace; import io.opencensus.trace.base.AttributeValue; import io.opencensus.trace.base.Link; import io.opencensus.trace.base.NetworkEvent; +import io.opencensus.trace.samplers.Samplers; import java.util.HashMap; import java.util.concurrent.TimeUnit; import org.openjdk.jmh.annotations.Benchmark; diff --git a/benchmarks/src/jmh/java/io/opencensus/trace/StartEndSpanBenchmark.java b/benchmarks/src/jmh/java/io/opencensus/trace/StartEndSpanBenchmark.java index 9a51ffbe..0a86b6f8 100644 --- a/benchmarks/src/jmh/java/io/opencensus/trace/StartEndSpanBenchmark.java +++ b/benchmarks/src/jmh/java/io/opencensus/trace/StartEndSpanBenchmark.java @@ -13,6 +13,7 @@ package io.opencensus.trace; +import io.opencensus.trace.samplers.Samplers; import java.util.concurrent.TimeUnit; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; diff --git a/core_impl/src/main/java/io/opencensus/trace/SpanFactoryImpl.java b/core_impl/src/main/java/io/opencensus/trace/SpanFactoryImpl.java index 3f087799..d9aadb5e 100644 --- a/core_impl/src/main/java/io/opencensus/trace/SpanFactoryImpl.java +++ b/core_impl/src/main/java/io/opencensus/trace/SpanFactoryImpl.java @@ -20,7 +20,9 @@ import io.opencensus.internal.TimestampConverter; import io.opencensus.trace.Span.Options; import io.opencensus.trace.base.Link; import io.opencensus.trace.base.Link.Type; +import io.opencensus.trace.base.Sampler; import io.opencensus.trace.base.SpanId; +import io.opencensus.trace.base.StartSpanOptions; import io.opencensus.trace.base.TraceId; import io.opencensus.trace.base.TraceOptions; import io.opencensus.trace.config.TraceConfig; diff --git a/core_impl/src/test/java/io/opencensus/trace/SpanFactoryImplTest.java b/core_impl/src/test/java/io/opencensus/trace/SpanFactoryImplTest.java index 4e718cf8..74bc2d86 100644 --- a/core_impl/src/test/java/io/opencensus/trace/SpanFactoryImplTest.java +++ b/core_impl/src/test/java/io/opencensus/trace/SpanFactoryImplTest.java @@ -20,11 +20,13 @@ import io.opencensus.internal.TestClock; import io.opencensus.trace.Span.Options; import io.opencensus.trace.SpanImpl.StartEndHandler; import io.opencensus.trace.base.SpanId; +import io.opencensus.trace.base.StartSpanOptions; import io.opencensus.trace.base.TraceId; import io.opencensus.trace.base.TraceOptions; import io.opencensus.trace.config.TraceConfig; import io.opencensus.trace.config.TraceParams; import io.opencensus.trace.internal.RandomHandler; +import io.opencensus.trace.samplers.Samplers; import java.util.Random; import org.junit.Before; import org.junit.Test; diff --git a/core_impl/src/test/java/io/opencensus/trace/config/TraceConfigImplTest.java b/core_impl/src/test/java/io/opencensus/trace/config/TraceConfigImplTest.java index 24e78b2b..a76e180f 100644 --- a/core_impl/src/test/java/io/opencensus/trace/config/TraceConfigImplTest.java +++ b/core_impl/src/test/java/io/opencensus/trace/config/TraceConfigImplTest.java @@ -15,7 +15,7 @@ package io.opencensus.trace.config; import static com.google.common.truth.Truth.assertThat; -import io.opencensus.trace.Samplers; +import io.opencensus.trace.samplers.Samplers; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -- cgit v1.2.3