diff options
author | sebright <sebright@google.com> | 2017-10-23 17:13:14 -0700 |
---|---|---|
committer | Bogdan Drutu <bdrutu@google.com> | 2017-10-23 17:13:14 -0700 |
commit | 92e363fb2daa1a8aee308d3bd5fc20c9e83eeab2 (patch) | |
tree | 34cf4a254947a69b24ed522f997d15c072bfe8e4 /api/src/main/java/io | |
parent | e69bb99d61c529fc9ad8c0be83d1f438c93a63af (diff) | |
download | opencensus-java-92e363fb2daa1a8aee308d3bd5fc20c9e83eeab2.tar.gz |
Move stats and tags packages to opencensus-api to prepare for release. (#723)
Diffstat (limited to 'api/src/main/java/io')
30 files changed, 3308 insertions, 0 deletions
diff --git a/api/src/main/java/io/opencensus/stats/Aggregation.java b/api/src/main/java/io/opencensus/stats/Aggregation.java new file mode 100644 index 00000000..f12517c8 --- /dev/null +++ b/api/src/main/java/io/opencensus/stats/Aggregation.java @@ -0,0 +1,179 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.stats; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.auto.value.AutoValue; +import io.opencensus.common.Function; +import javax.annotation.concurrent.Immutable; + +/** + * {@link Aggregation} is the process of combining a certain set of {@code MeasureValue}s + * for a given {@code Measure} into an {@link AggregationData}. + * + * <p>{@link Aggregation} currently supports 4 types of basic aggregation: + * <ul> + * <li>Sum + * <li>Count + * <li>Mean + * <li>Distribution + * </ul> + * + * <p>When creating a {@link View}, one {@link Aggregation} needs to be specified as how to + * aggregate {@code MeasureValue}s. + */ +@Immutable +public abstract class Aggregation { + + private Aggregation() { + } + + /** + * Applies the given match function to the underlying data type. + */ + public abstract <T> T match( + Function<? super Sum, T> p0, + Function<? super Count, T> p1, + Function<? super Mean, T> p2, + Function<? super Distribution, T> p3, + Function<? super Aggregation, T> defaultFunction); + + /** Calculate sum on aggregated {@code MeasureValue}s. */ + @Immutable + @AutoValue + public abstract static class Sum extends Aggregation { + + Sum() { + } + + private static final Sum INSTANCE = new AutoValue_Aggregation_Sum(); + + /** + * Construct a {@code Sum}. + * + * @return a new {@code Sum}. + */ + public static Sum create() { + return INSTANCE; + } + + @Override + public final <T> T match( + Function<? super Sum, T> p0, + Function<? super Count, T> p1, + Function<? super Mean, T> p2, + Function<? super Distribution, T> p3, + Function<? super Aggregation, T> defaultFunction) { + return p0.apply(this); + } + } + + /** Calculate count on aggregated {@code MeasureValue}s. */ + @Immutable + @AutoValue + public abstract static class Count extends Aggregation { + + Count() { + } + + private static final Count INSTANCE = new AutoValue_Aggregation_Count(); + + /** + * Construct a {@code Count}. + * + * @return a new {@code Count}. + */ + public static Count create() { + return INSTANCE; + } + + @Override + public final <T> T match( + Function<? super Sum, T> p0, + Function<? super Count, T> p1, + Function<? super Mean, T> p2, + Function<? super Distribution, T> p3, + Function<? super Aggregation, T> defaultFunction) { + return p1.apply(this); + } + } + + /** Calculate mean on aggregated {@code MeasureValue}s. */ + @Immutable + @AutoValue + public abstract static class Mean extends Aggregation { + + Mean() { + } + + private static final Mean INSTANCE = new AutoValue_Aggregation_Mean(); + + /** + * Construct a {@code Mean}. + * + * @return a new {@code Mean}. + */ + public static Mean create() { + return INSTANCE; + } + + @Override + public final <T> T match( + Function<? super Sum, T> p0, + Function<? super Count, T> p1, + Function<? super Mean, T> p2, + Function<? super Distribution, T> p3, + Function<? super Aggregation, T> defaultFunction) { + return p2.apply(this); + } + } + + /** + * Calculate distribution stats on aggregated {@code MeasureValue}s. Distribution includes + * mean, count, histogram, min, max and sum of squared deviations. + */ + @Immutable + @AutoValue + public abstract static class Distribution extends Aggregation { + + Distribution() { + } + + /** + * Construct a {@code Distribution}. + * + * @return a new {@code Distribution}. + */ + public static Distribution create(BucketBoundaries bucketBoundaries) { + checkNotNull(bucketBoundaries, "bucketBoundaries should not be null."); + return new AutoValue_Aggregation_Distribution(bucketBoundaries); + } + + public abstract BucketBoundaries getBucketBoundaries(); + + @Override + public final <T> T match( + Function<? super Sum, T> p0, + Function<? super Count, T> p1, + Function<? super Mean, T> p2, + Function<? super Distribution, T> p3, + Function<? super Aggregation, T> defaultFunction) { + return p3.apply(this); + } + } +} diff --git a/api/src/main/java/io/opencensus/stats/AggregationData.java b/api/src/main/java/io/opencensus/stats/AggregationData.java new file mode 100644 index 00000000..fdf09592 --- /dev/null +++ b/api/src/main/java/io/opencensus/stats/AggregationData.java @@ -0,0 +1,312 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.stats; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.auto.value.AutoValue; +import io.opencensus.common.Function; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.annotation.concurrent.Immutable; + +/** + * {@link AggregationData} is the result of applying a given {@link Aggregation} to a set of + * {@code MeasureValue}s. + * + * <p>{@link AggregationData} currently supports 5 types of basic aggregation values: + * <ul> + * <li>SumDataDouble + * <li>SumDataLong + * <li>CountData + * <li>MeanData + * <li>DistributionData + * </ul> + * + * <p>{@link ViewData} will contain one {@link AggregationData}, corresponding to its + * {@link Aggregation} definition in {@link View}. + */ +@Immutable +public abstract class AggregationData { + + private AggregationData() { + } + + /** + * Applies the given match function to the underlying data type. + */ + public abstract <T> T match( + Function<? super SumDataDouble, T> p0, + Function<? super SumDataLong, T> p1, + Function<? super CountData, T> p2, + Function<? super MeanData, T> p3, + Function<? super DistributionData, T> p4, + Function<? super AggregationData, T> defaultFunction); + + /** The sum value of aggregated {@code MeasureValueDouble}s. */ + @Immutable + @AutoValue + public abstract static class SumDataDouble extends AggregationData { + + SumDataDouble() { + } + + /** + * Creates a {@code SumDataDouble}. + * + * @param sum the aggregated sum. + * @return a {@code SumDataDouble}. + */ + public static SumDataDouble create(double sum) { + return new AutoValue_AggregationData_SumDataDouble(sum); + } + + /** + * Returns the aggregated sum. + * + * @return the aggregated sum. + */ + public abstract double getSum(); + + @Override + public final <T> T match( + Function<? super SumDataDouble, T> p0, + Function<? super SumDataLong, T> p1, + Function<? super CountData, T> p2, + Function<? super MeanData, T> p3, + Function<? super DistributionData, T> p4, + Function<? super AggregationData, T> defaultFunction) { + return p0.apply(this); + } + } + + /** The sum value of aggregated {@code MeasureValueLong}s. */ + @Immutable + @AutoValue + public abstract static class SumDataLong extends AggregationData { + + SumDataLong() { + } + + /** + * Creates a {@code SumDataLong}. + * + * @param sum the aggregated sum. + * @return a {@code SumDataLong}. + */ + public static SumDataLong create(long sum) { + return new AutoValue_AggregationData_SumDataLong(sum); + } + + /** + * Returns the aggregated sum. + * + * @return the aggregated sum. + */ + public abstract long getSum(); + + @Override + public final <T> T match( + Function<? super SumDataDouble, T> p0, + Function<? super SumDataLong, T> p1, + Function<? super CountData, T> p2, + Function<? super MeanData, T> p3, + Function<? super DistributionData, T> p4, + Function<? super AggregationData, T> defaultFunction) { + return p1.apply(this); + } + } + + /** The count value of aggregated {@code MeasureValue}s. */ + @Immutable + @AutoValue + public abstract static class CountData extends AggregationData { + + CountData() { + } + + /** + * Creates a {@code CountData}. + * + * @param count the aggregated count. + * @return a {@code CountData}. + */ + public static CountData create(long count) { + return new AutoValue_AggregationData_CountData(count); + } + + /** + * Returns the aggregated count. + * + * @return the aggregated count. + */ + public abstract long getCount(); + + @Override + public final <T> T match( + Function<? super SumDataDouble, T> p0, + Function<? super SumDataLong, T> p1, + Function<? super CountData, T> p2, + Function<? super MeanData, T> p3, + Function<? super DistributionData, T> p4, + Function<? super AggregationData, T> defaultFunction) { + return p2.apply(this); + } + } + + /** The mean value of aggregated {@code MeasureValue}s. */ + @Immutable + @AutoValue + public abstract static class MeanData extends AggregationData { + + MeanData() { + } + + /** + * Creates a {@code MeanData}. + * + * @param mean the aggregated mean. + * @param count the aggregated count. + * @return a {@code MeanData}. + */ + public static MeanData create(double mean, long count) { + return new AutoValue_AggregationData_MeanData(mean, count); + } + + /** + * Returns the aggregated mean. + * + * @return the aggregated mean. + */ + public abstract double getMean(); + + /** + * Returns the aggregated count. + * + * @return the aggregated count. + */ + public abstract long getCount(); + + @Override + public final <T> T match( + Function<? super SumDataDouble, T> p0, + Function<? super SumDataLong, T> p1, + Function<? super CountData, T> p2, + Function<? super MeanData, T> p3, + Function<? super DistributionData, T> p4, + Function<? super AggregationData, T> defaultFunction) { + return p3.apply(this); + } + } + + /** + * The distribution stats of aggregated {@code MeasureValue}s. Distribution stats include mean, + * count, histogram, min, max and sum of squared deviations. + */ + @Immutable + @AutoValue + public abstract static class DistributionData extends AggregationData { + + DistributionData() { + } + + /** + * Creates a {@code DistributionData}. + * + * @param mean mean value. + * @param count count value. + * @param min min value. + * @param max max value. + * @param sumOfSquaredDeviations sum of squared deviations. + * @param bucketCounts histogram bucket counts. + * @return a {@code DistributionData}. + */ + public static DistributionData create( + double mean, long count, double min, double max, double sumOfSquaredDeviations, + long[] bucketCounts) { + if (min != Double.POSITIVE_INFINITY || max != Double.NEGATIVE_INFINITY) { + checkArgument(min <= max, "max should be greater or equal to min."); + } + + checkNotNull(bucketCounts, "bucket counts should not be null."); + List<Long> boxedBucketCounts = new ArrayList<Long>(); + for (long bucketCount : bucketCounts) { + boxedBucketCounts.add(bucketCount); + } + + return new AutoValue_AggregationData_DistributionData( + mean, count, min, max, sumOfSquaredDeviations, + Collections.unmodifiableList(boxedBucketCounts)); + } + + /** + * Returns the aggregated mean. + * + * @return the aggregated mean. + */ + public abstract double getMean(); + + /** + * Returns the aggregated count. + * + * @return the aggregated count. + */ + public abstract long getCount(); + + /** + * Returns the minimum of the population values. + * + * @return the minimum of the population values. + */ + public abstract double getMin(); + + /** + * Returns the maximum of the population values. + * + * @return the maximum of the population values. + */ + public abstract double getMax(); + + /** + * Returns the aggregated sum of squared deviations. + * + * @return the aggregated sum of squared deviations. + */ + public abstract double getSumOfSquaredDeviations(); + + /** + * Returns the aggregated bucket counts. The returned list is immutable, trying to update it + * will throw an {@code UnsupportedOperationException}. + * + * @return the aggregated bucket counts. + */ + public abstract List<Long> getBucketCounts(); + + @Override + public final <T> T match( + Function<? super SumDataDouble, T> p0, + Function<? super SumDataLong, T> p1, + Function<? super CountData, T> p2, + Function<? super MeanData, T> p3, + Function<? super DistributionData, T> p4, + Function<? super AggregationData, T> defaultFunction) { + return p4.apply(this); + } + } +} diff --git a/api/src/main/java/io/opencensus/stats/BucketBoundaries.java b/api/src/main/java/io/opencensus/stats/BucketBoundaries.java new file mode 100644 index 00000000..b9a808f4 --- /dev/null +++ b/api/src/main/java/io/opencensus/stats/BucketBoundaries.java @@ -0,0 +1,58 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.stats; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.auto.value.AutoValue; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.annotation.concurrent.Immutable; + +/** The bucket boundaries for a histogram. */ +@Immutable +@AutoValue +public abstract class BucketBoundaries { + + /** + * @param bucketBoundaries the boundaries for the buckets in the underlying histogram. + * @return a new {@code BucketBoundaries} with the specified boundaries. + * @throws NullPointerException if {@code bucketBoundaries} is null. + * @throws IllegalArgumentException if {@code bucketBoundaries} is not sorted. + */ + public static final BucketBoundaries create(List<Double> bucketBoundaries) { + checkNotNull(bucketBoundaries, "bucketBoundaries list should not be null."); + List<Double> bucketBoundariesCopy = new ArrayList<Double>(bucketBoundaries); // Deep copy. + // Check if sorted. + if (bucketBoundariesCopy.size() > 1) { + double lower = bucketBoundariesCopy.get(0); + for (int i = 1; i < bucketBoundariesCopy.size(); i++) { + double next = bucketBoundariesCopy.get(i); + checkArgument(lower < next, "Bucket boundaries not sorted."); + lower = next; + } + } + return new AutoValue_BucketBoundaries(Collections.unmodifiableList(bucketBoundariesCopy)); + } + + /** + * @return a list of histogram bucket boundaries. + */ + public abstract List<Double> getBoundaries(); +} diff --git a/api/src/main/java/io/opencensus/stats/Measure.java b/api/src/main/java/io/opencensus/stats/Measure.java new file mode 100644 index 00000000..e7a0ec96 --- /dev/null +++ b/api/src/main/java/io/opencensus/stats/Measure.java @@ -0,0 +1,150 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.stats; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.auto.value.AutoValue; +import com.google.common.annotations.VisibleForTesting; +import io.opencensus.common.Function; +import io.opencensus.internal.StringUtil; +import javax.annotation.concurrent.Immutable; + +/** The definition of the {@link Measurement} that is taken by OpenCensus library. */ +@Immutable +public abstract class Measure { + + @VisibleForTesting static final int NAME_MAX_LENGTH = 256; + + /** + * Applies the given match function to the underlying data type. + */ + public abstract <T> T match( + Function<? super MeasureDouble, T> p0, + Function<? super MeasureLong, T> p1, + Function<? super Measure, T> defaultFunction); + + /** + * Name of measure, as a {@code String}. Should be a ASCII string with a length no greater than + * 256 characters. + * + * <p>Suggested format for name: {@code <web_host>/<path>}. + */ + public abstract String getName(); + + /** + * Detailed description of the measure, used in documentation. + */ + public abstract String getDescription(); + + /** + * The units in which {@link Measure} values are measured. + * + * <p>The suggested grammar for a unit is as follows: + * Expression = Component { "." Component } { "/" Component } ; + * Component = [ PREFIX ] UNIT [ Annotation ] | Annotation | "1" ; + * Annotation = "{" NAME "}" ; + * For example, string “MBy{transmitted}/ms” stands for megabytes per milliseconds, and the + * annotation transmitted inside {} is just a comment of the unit. + */ + // TODO(songya): determine whether we want to check the grammar on string unit. + public abstract String getUnit(); + + // Prevents this class from being subclassed anywhere else. + private Measure() { + } + + /** {@link Measure} with {@code Double} typed values. */ + @Immutable + @AutoValue + public abstract static class MeasureDouble extends Measure { + + MeasureDouble() { + } + + /** + * Constructs a new {@link MeasureDouble}. + * + * @param name name of {@code Measure}. Suggested format: {@code <web_host>/<path>}. + * @param description description of {@code Measure}. + * @param unit unit of {@code Measure}. + * @return a {@code MeasureDouble}. + */ + public static MeasureDouble create(String name, String description, String unit) { + checkArgument(StringUtil.isPrintableString(name) && name.length() <= NAME_MAX_LENGTH, + "Name should be a ASCII string with a length no greater than 256 characters."); + return new AutoValue_Measure_MeasureDouble(name, description, unit); + } + + @Override + public <T> T match( + Function<? super MeasureDouble, T> p0, + Function<? super MeasureLong, T> p1, + Function<? super Measure, T> defaultFunction) { + return p0.apply(this); + } + + @Override + public abstract String getName(); + + @Override + public abstract String getDescription(); + + @Override + public abstract String getUnit(); + } + + /** {@link Measure} with {@code Long} typed values. */ + @Immutable + @AutoValue + public abstract static class MeasureLong extends Measure { + + MeasureLong() { + } + + /** + * Constructs a new {@link MeasureLong}. + * + * @param name name of {@code Measure}. Suggested format: {@code <web_host>/<path>}. + * @param description description of {@code Measure}. + * @param unit unit of {@code Measure}. + * @return a {@code MeasureLong}. + */ + public static MeasureLong create(String name, String description, String unit) { + checkArgument(StringUtil.isPrintableString(name) && name.length() <= NAME_MAX_LENGTH, + "Name should be a ASCII string with a length no greater than 256 characters."); + return new AutoValue_Measure_MeasureLong(name, description, unit); + } + + @Override + public <T> T match( + Function<? super MeasureDouble, T> p0, + Function<? super MeasureLong, T> p1, + Function<? super Measure, T> defaultFunction) { + return p1.apply(this); + } + + @Override + public abstract String getName(); + + @Override + public abstract String getDescription(); + + @Override + public abstract String getUnit(); + } +} diff --git a/api/src/main/java/io/opencensus/stats/MeasureMap.java b/api/src/main/java/io/opencensus/stats/MeasureMap.java new file mode 100644 index 00000000..0cc972f0 --- /dev/null +++ b/api/src/main/java/io/opencensus/stats/MeasureMap.java @@ -0,0 +1,128 @@ +/* + * Copyright 2016-17, OpenCensus 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.opencensus.stats; + +import io.opencensus.stats.Measure.MeasureDouble; +import io.opencensus.stats.Measure.MeasureLong; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * A map from {@link Measure}'s to measured values. + */ +public final class MeasureMap { + + /** + * Returns a {@link Builder} for the {@link MeasureMap} class. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns an {@link Iterator} over the measure/value mappings in this {@link MeasureMap}. + * The {@code Iterator} does not support {@link Iterator#remove()}. + */ + public Iterator<Measurement> iterator() { + return new MeasureMapIterator(); + } + + private final ArrayList<Measurement> measurements; + + private MeasureMap(ArrayList<Measurement> measurements) { + this.measurements = measurements; + } + + /** + * Builder for the {@link MeasureMap} class. + */ + public static class Builder { + /** + * Associates the {@link MeasureDouble} with the given value. Subsequent updates to the + * same {@link MeasureDouble} will overwrite the previous value. + * + * @param measure the {@link MeasureDouble} + * @param value the value to be associated with {@code measure} + * @return this + */ + public Builder put(MeasureDouble measure, double value) { + measurements.add(Measurement.MeasurementDouble.create(measure, value)); + return this; + } + + /** + * Associates the {@link MeasureLong} with the given value. Subsequent updates to the + * same {@link MeasureLong} will overwrite the previous value. + * + * @param measure the {@link MeasureLong} + * @param value the value to be associated with {@code measure} + * @return this + */ + public Builder put(MeasureLong measure, long value) { + measurements.add(Measurement.MeasurementLong.create(measure, value)); + return this; + } + + /** + * Constructs a {@link MeasureMap} from the current measurements. + */ + public MeasureMap build() { + // Note: this makes adding measurements quadratic but is fastest for the sizes of + // MeasureMaps that we should see. We may want to go to a strategy of sort/eliminate + // for larger MeasureMaps. + for (int i = measurements.size() - 1; i >= 0; i--) { + for (int j = i - 1; j >= 0; j--) { + if (measurements.get(i).getMeasure() == measurements.get(j).getMeasure()) { + measurements.remove(j); + j--; + } + } + } + return new MeasureMap(measurements); + } + + private final ArrayList<Measurement> measurements = new ArrayList<Measurement>(); + + private Builder() { + } + } + + // Provides an unmodifiable Iterator over this instance's measurements. + private final class MeasureMapIterator implements Iterator<Measurement> { + @Override + public boolean hasNext() { + return position < length; + } + + @Override + public Measurement next() { + if (position >= measurements.size()) { + throw new NoSuchElementException(); + } + return measurements.get(position++); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + private final int length = measurements.size(); + private int position = 0; + } +} diff --git a/api/src/main/java/io/opencensus/stats/Measurement.java b/api/src/main/java/io/opencensus/stats/Measurement.java new file mode 100644 index 00000000..10fc5888 --- /dev/null +++ b/api/src/main/java/io/opencensus/stats/Measurement.java @@ -0,0 +1,99 @@ +/* + * Copyright 2016-17, OpenCensus 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.opencensus.stats; + +import com.google.auto.value.AutoValue; +import io.opencensus.common.Function; +import io.opencensus.stats.Measure.MeasureDouble; +import io.opencensus.stats.Measure.MeasureLong; +import javax.annotation.concurrent.Immutable; + +/** Immutable representation of a Measurement. */ +@Immutable +public abstract class Measurement { + + /** + * Applies the given match function to the underlying data type. + */ + public abstract <T> T match( + Function<? super MeasurementDouble, T> p0, + Function<? super MeasurementLong, T> p1, + Function<? super Measurement, T> defaultFunction); + + /** + * Extracts the measured {@link Measure}. + */ + public abstract Measure getMeasure(); + + // Prevents this class from being subclassed anywhere else. + private Measurement() { + } + + /** {@code Double} typed {@link Measurement}. */ + @Immutable + @AutoValue + public abstract static class MeasurementDouble extends Measurement { + MeasurementDouble() {} + + /** + * Constructs a new {@link MeasurementDouble}. + */ + public static MeasurementDouble create(MeasureDouble measure, double value) { + return new AutoValue_Measurement_MeasurementDouble(measure, value); + } + + @Override + public abstract MeasureDouble getMeasure(); + + public abstract double getValue(); + + @Override + public <T> T match( + Function<? super MeasurementDouble, T> p0, + Function<? super MeasurementLong, T> p1, + Function<? super Measurement, T> defaultFunction) { + return p0.apply(this); + } + } + + /** {@code Long} typed {@link Measurement}. */ + @Immutable + @AutoValue + public abstract static class MeasurementLong extends Measurement { + MeasurementLong() {} + + /** + * Constructs a new {@link MeasurementLong}. + */ + public static MeasurementLong create(MeasureLong measure, long value) { + return new AutoValue_Measurement_MeasurementLong(measure, value); + } + + @Override + public abstract MeasureLong getMeasure(); + + public abstract long getValue(); + + @Override + public <T> T match( + Function<? super MeasurementDouble, T> p0, + Function<? super MeasurementLong, T> p1, + Function<? super Measurement, T> defaultFunction) { + return p1.apply(this); + } + } +} diff --git a/api/src/main/java/io/opencensus/stats/NoopStats.java b/api/src/main/java/io/opencensus/stats/NoopStats.java new file mode 100644 index 00000000..79ddb359 --- /dev/null +++ b/api/src/main/java/io/opencensus/stats/NoopStats.java @@ -0,0 +1,151 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.stats; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import io.opencensus.common.Functions; +import io.opencensus.common.Timestamp; +import io.opencensus.stats.ViewData.AggregationWindowData; +import io.opencensus.stats.ViewData.AggregationWindowData.CumulativeData; +import io.opencensus.stats.ViewData.AggregationWindowData.IntervalData; +import io.opencensus.tags.TagContext; +import io.opencensus.tags.TagValue; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.Immutable; +import javax.annotation.concurrent.ThreadSafe; + +/** No-op implementations of stats classes. */ +final class NoopStats { + + private NoopStats() {} + + /** + * Returns a {@code StatsComponent} that has a no-op implementation for {@link StatsRecorder}. + * + * @return a {@code StatsComponent} that has a no-op implementation for {@code StatsRecorder}. + */ + static StatsComponent newNoopStatsComponent() { + return new NoopStatsComponent(); + } + + /** + * Returns a {@code StatsRecorder} that does not record any data. + * + * @return a {@code StatsRecorder} that does not record any data. + */ + static StatsRecorder getNoopStatsRecorder() { + return NoopStatsRecorder.INSTANCE; + } + + /** + * Returns a {@code ViewManager} that maintains a map of views, but always returns empty {@link + * ViewData}s. + * + * @return a {@code ViewManager} that maintains a map of views, but always returns empty {@code + * ViewData}s. + */ + static ViewManager newNoopViewManager() { + return new NoopViewManager(); + } + + @ThreadSafe + private static final class NoopStatsComponent extends StatsComponent { + private final ViewManager viewManager = newNoopViewManager(); + + @Override + public ViewManager getViewManager() { + return viewManager; + } + + @Override + public StatsRecorder getStatsRecorder() { + return getNoopStatsRecorder(); + } + + @Override + public StatsCollectionState getState() { + return StatsCollectionState.DISABLED; + } + + @Override + public void setState(StatsCollectionState state) { + Preconditions.checkNotNull(state, "state"); + } + } + + @Immutable + private static final class NoopStatsRecorder extends StatsRecorder { + static final StatsRecorder INSTANCE = new NoopStatsRecorder(); + + @Override + public void record(TagContext tags, MeasureMap measureValues) { + checkNotNull(tags, "tags"); + checkNotNull(measureValues, "measureValues"); + } + } + + @ThreadSafe + private static final class NoopViewManager extends ViewManager { + private static final Timestamp ZERO_TIMESTAMP = Timestamp.create(0, 0); + + @GuardedBy("views") + private final Map<View.Name, View> views = Maps.newHashMap(); + + @Override + public void registerView(View newView) { + checkNotNull(newView, "newView"); + synchronized (views) { + View existing = views.get(newView.getName()); + checkArgument( + existing == null || newView.equals(existing), + "A different view with the same name already exists."); + if (existing == null) { + views.put(newView.getName(), newView); + } + } + } + + @Override + public ViewData getView(View.Name name) { + checkNotNull(name, "name"); + synchronized (views) { + View view = views.get(name); + if (view == null) { + throw new IllegalArgumentException("View is not registered."); + } else { + return ViewData.create( + view, + Collections.<List<TagValue>, AggregationData>emptyMap(), + view.getWindow() + .match( + Functions.<AggregationWindowData>returnConstant( + CumulativeData.create(ZERO_TIMESTAMP, ZERO_TIMESTAMP)), + Functions.<AggregationWindowData>returnConstant( + IntervalData.create(ZERO_TIMESTAMP)), + Functions.<AggregationWindowData>throwAssertionError())); + } + } + } + } +} diff --git a/api/src/main/java/io/opencensus/stats/Stats.java b/api/src/main/java/io/opencensus/stats/Stats.java new file mode 100644 index 00000000..016455e9 --- /dev/null +++ b/api/src/main/java/io/opencensus/stats/Stats.java @@ -0,0 +1,95 @@ +/* + * Copyright 2016-17, OpenCensus 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.opencensus.stats; + +import com.google.common.annotations.VisibleForTesting; +import io.opencensus.internal.Provider; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** Class for accessing the default {@link StatsComponent}. */ +public final class Stats { + private static final Logger logger = Logger.getLogger(Stats.class.getName()); + + private static final StatsComponent statsComponent = + loadStatsComponent(StatsComponent.class.getClassLoader()); + + /** Returns the default {@link StatsRecorder}. */ + public static StatsRecorder getStatsRecorder() { + return statsComponent.getStatsRecorder(); + } + + /** Returns the default {@link ViewManager}. */ + public static ViewManager getViewManager() { + return statsComponent.getViewManager(); + } + + /** + * Returns the current {@code StatsCollectionState}. + * + * <p>When no implementation is available, {@code getState} always returns {@link + * StatsCollectionState#DISABLED}. + * + * @return the current {@code StatsCollectionState}. + */ + public static StatsCollectionState getState() { + return statsComponent.getState(); + } + + /** + * Sets the current {@code StatsCollectionState}. + * + * <p>When no implementation is available, {@code setState} has no effect. + * + * @param state the new {@code StatsCollectionState}. + */ + public static void setState(StatsCollectionState state) { + statsComponent.setState(state); + } + + // Any provider that may be used for StatsComponent can be added here. + @VisibleForTesting + static StatsComponent loadStatsComponent(ClassLoader classLoader) { + try { + // Call Class.forName with literal string name of the class to help shading tools. + return Provider.createInstance( + Class.forName("io.opencensus.impl.stats.StatsComponentImpl", true, classLoader), + StatsComponent.class); + } catch (ClassNotFoundException e) { + logger.log( + Level.FINE, + "Couldn't load full implementation for StatsComponent, now trying to load lite " + + "implementation.", + e); + } + try { + // Call Class.forName with literal string name of the class to help shading tools. + return Provider.createInstance( + Class.forName("io.opencensus.impllite.stats.StatsComponentImplLite", true, classLoader), + StatsComponent.class); + } catch (ClassNotFoundException e) { + logger.log( + Level.FINE, + "Couldn't load lite implementation for StatsComponent, now using " + + "default implementation for StatsComponent.", + e); + } + return NoopStats.newNoopStatsComponent(); + } + + private Stats() {} +} diff --git a/api/src/main/java/io/opencensus/stats/StatsCollectionState.java b/api/src/main/java/io/opencensus/stats/StatsCollectionState.java new file mode 100644 index 00000000..7e5641b1 --- /dev/null +++ b/api/src/main/java/io/opencensus/stats/StatsCollectionState.java @@ -0,0 +1,36 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.stats; + +/** State of the {@link StatsComponent}. */ +public enum StatsCollectionState { + + /** + * State that fully enables stats collection. + * + * <p>The {@link StatsComponent} collects stats for registered views. + */ + ENABLED, + + /** + * State that disables stats collection. + * + * <p>The {@link StatsComponent} does not need to collect stats for registered views and may + * return empty {@link ViewData}s from {@link ViewManager#getView(View.Name)}. + */ + DISABLED +} diff --git a/api/src/main/java/io/opencensus/stats/StatsComponent.java b/api/src/main/java/io/opencensus/stats/StatsComponent.java new file mode 100644 index 00000000..69fbcb5c --- /dev/null +++ b/api/src/main/java/io/opencensus/stats/StatsComponent.java @@ -0,0 +1,50 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.stats; + +/** + * Class that holds the implementations for {@link ViewManager} and {@link StatsRecorder}. + * + * <p>All objects returned by methods on {@code StatsComponent} are cacheable. + */ +public abstract class StatsComponent { + + /** Returns the default {@link ViewManager}. */ + public abstract ViewManager getViewManager(); + + /** Returns the default {@link StatsRecorder}. */ + public abstract StatsRecorder getStatsRecorder(); + + /** + * Returns the current {@code StatsCollectionState}. + * + * <p>When no implementation is available, {@code getState} always returns {@link + * StatsCollectionState#DISABLED}. + * + * @return the current {@code StatsCollectionState}. + */ + public abstract StatsCollectionState getState(); + + /** + * Sets the current {@code StatsCollectionState}. + * + * <p>When no implementation is available, {@code setState} has no effect. + * + * @param state the new {@code StatsCollectionState}. + */ + public abstract void setState(StatsCollectionState state); +} diff --git a/api/src/main/java/io/opencensus/stats/StatsRecorder.java b/api/src/main/java/io/opencensus/stats/StatsRecorder.java new file mode 100644 index 00000000..cab21c29 --- /dev/null +++ b/api/src/main/java/io/opencensus/stats/StatsRecorder.java @@ -0,0 +1,42 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.stats; + +import io.opencensus.tags.TagContext; +import io.opencensus.tags.unsafe.ContextUtils; + +/** Provides methods to record stats against tags. */ +public abstract class StatsRecorder { + + /** + * Records a set of measurements with the tags in the current context. + * + * @param measureValues the measurements to record. + */ + public final void record(MeasureMap measureValues) { + // Use the context key directly, to avoid depending on the tags implementation. + record(ContextUtils.TAG_CONTEXT_KEY.get(), measureValues); + } + + /** + * Records a set of measurements with a set of tags. + * + * @param tags the tags associated with the measurements. + * @param measureValues the measurements to record. + */ + public abstract void record(TagContext tags, MeasureMap measureValues); +} diff --git a/api/src/main/java/io/opencensus/stats/View.java b/api/src/main/java/io/opencensus/stats/View.java new file mode 100644 index 00000000..31b06461 --- /dev/null +++ b/api/src/main/java/io/opencensus/stats/View.java @@ -0,0 +1,229 @@ +/* + * Copyright 2016-17, OpenCensus 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.opencensus.stats; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.auto.value.AutoValue; +import com.google.common.annotations.VisibleForTesting; +import io.opencensus.common.Duration; +import io.opencensus.common.Function; +import io.opencensus.internal.StringUtil; +import io.opencensus.tags.TagKey; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import javax.annotation.concurrent.Immutable; + +/** + * A View specifies an aggregation and a set of tag keys. The aggregation will be broken + * down by the unique set of matching tag values for each measure. + */ +@Immutable +@AutoValue +public abstract class View { + + @VisibleForTesting static final int NAME_MAX_LENGTH = 256; + + View() { + } + + /** + * Name of view. Must be unique. + */ + public abstract Name getName(); + + /** + * More detailed description, for documentation purposes. + */ + public abstract String getDescription(); + + /** + * Measure type of this view. + */ + public abstract Measure getMeasure(); + + /** + * The {@link Aggregation} associated with this {@link View}. + */ + public abstract Aggregation getAggregation(); + + /** + * Columns (a.k.a Tag Keys) to match with the associated {@link Measure}. + * + * <p>{@link Measure} will be recorded in a "greedy" way. That is, every view aggregates every + * measure. This is similar to doing a GROUPBY on view’s columns. Columns must be unique. + */ + public abstract List<TagKey> getColumns(); + + /** + * Returns the time {@link AggregationWindow} for this {@code View}. + * + * @return the time {@link AggregationWindow}. + */ + public abstract AggregationWindow getWindow(); + + /** + * Constructs a new {@link View}. + * + * @param name the {@link Name} of view. Must be unique. + * @param description the description of view. + * @param measure the {@link Measure} to be aggregated by this view. + * @param aggregation the basic {@link Aggregation} that this view will support. + * @param columns the {@link TagKey}s that this view will aggregate on. Columns should not contain + * duplicates. + * @param window the {@link AggregationWindow} of view. + * @return a new {@link View}. + */ + public static View create( + Name name, + String description, + Measure measure, + Aggregation aggregation, + List<? extends TagKey> columns, + AggregationWindow window) { + checkArgument(new HashSet<TagKey>(columns).size() == columns.size(), + "Columns have duplicate."); + + return new AutoValue_View( + name, + description, + measure, + aggregation, + Collections.unmodifiableList(new ArrayList<TagKey>(columns)), + window); + } + + /** + * The name of a {@code View}. + */ + // This type should be used as the key when associating data with Views. + @Immutable + @AutoValue + public abstract static class Name { + + Name() {} + + /** + * Returns the name as a {@code String}. + * + * @return the name as a {@code String}. + */ + public abstract String asString(); + + /** + * Creates a {@code View.Name} from a {@code String}. Should be a ASCII string with a length + * no greater than 256 characters. + * + * <p>Suggested format for name: {@code <web_host>/<path>}. + * + * @param name the name {@code String}. + * @return a {@code View.Name} with the given name {@code String}. + */ + public static Name create(String name) { + checkArgument(StringUtil.isPrintableString(name) && name.length() <= NAME_MAX_LENGTH, + "Name should be a ASCII string with a length no greater than 256 characters."); + return new AutoValue_View_Name(name); + } + } + + /** The time window for a {@code View}. */ + @Immutable + public abstract static class AggregationWindow { + + private AggregationWindow() {} + + /** + * Applies the given match function to the underlying data type. + */ + public abstract <T> T match( + Function<? super Cumulative, T> p0, + Function<? super Interval, T> p1, + Function<? super AggregationWindow, T> defaultFunction); + + /** Cumulative (infinite interval) time {@code AggregationWindow}. */ + @Immutable + @AutoValue + public abstract static class Cumulative extends AggregationWindow { + + private static final Cumulative CUMULATIVE = + new AutoValue_View_AggregationWindow_Cumulative(); + + Cumulative() {} + + /** + * Constructs a cumulative {@code AggregationWindow} that does not have an explicit {@code + * Duration}. Instead, cumulative {@code AggregationWindow} always has an interval of infinite + * {@code Duration}. + * + * @return a cumulative {@code AggregationWindow}. + */ + public static Cumulative create() { + return CUMULATIVE; + } + + @Override + public final <T> T match( + Function<? super Cumulative, T> p0, + Function<? super Interval, T> p1, + Function<? super AggregationWindow, T> defaultFunction) { + return p0.apply(this); + } + } + + /** Interval (finite interval) time {@code AggregationWindow.} */ + @Immutable + @AutoValue + public abstract static class Interval extends AggregationWindow { + + private static final Duration ZERO = Duration.create(0, 0); + + Interval() {} + + /** + * Returns the {@code Duration} associated with this {@code Interval}. + * + * @return a {@code Duration}. + */ + public abstract Duration getDuration(); + + + /** + * Constructs an interval {@code AggregationWindow} that has a finite explicit {@code + * Duration}. + * + * <p>The {@code Duration} should be able to round to milliseconds. Currently interval window + * cannot have smaller {@code Duration} such as microseconds or nanoseconds. + * + * @return an interval {@code AggregationWindow}. + */ + public static Interval create(Duration duration) { + checkArgument(duration.compareTo(ZERO) > 0, "Duration must be positive"); + return new AutoValue_View_AggregationWindow_Interval(duration); + } + + @Override + public final <T> T match( + Function<? super Cumulative, T> p0, + Function<? super Interval, T> p1, + Function<? super AggregationWindow, T> defaultFunction) { + return p1.apply(this); + } + } + } +} diff --git a/api/src/main/java/io/opencensus/stats/ViewData.java b/api/src/main/java/io/opencensus/stats/ViewData.java new file mode 100644 index 00000000..e4872091 --- /dev/null +++ b/api/src/main/java/io/opencensus/stats/ViewData.java @@ -0,0 +1,182 @@ +/* + * Copyright 2016-17, OpenCensus 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.opencensus.stats; + +import com.google.auto.value.AutoValue; +import com.google.common.collect.Maps; +import io.opencensus.common.Function; +import io.opencensus.common.Functions; +import io.opencensus.common.Timestamp; +import io.opencensus.tags.TagValue; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import javax.annotation.concurrent.Immutable; + +/** The aggregated data for a particular {@link View}. */ +@Immutable +@AutoValue +public abstract class ViewData { + + // Prevents this class from being subclassed anywhere else. + ViewData() {} + + /** The {@link View} associated with this {@link ViewData}. */ + public abstract View getView(); + + /** + * The {@link AggregationData} grouped by combination of tag values, associated with this + * {@link ViewData}. + */ + public abstract Map<List<TagValue>, AggregationData> getAggregationMap(); + + /** + * Returns the {@link AggregationWindowData} associated with this {@link ViewData}. + * + * @return the {@code AggregationWindowData}. + */ + public abstract AggregationWindowData getWindowData(); + + /** Constructs a new {@link ViewData}. */ + public static ViewData create( + View view, + Map<? extends List<? extends TagValue>, ? extends AggregationData> map, + final AggregationWindowData windowData) { + view.getWindow() + .match( + new Function<View.AggregationWindow.Cumulative, Void>() { + @Override + public Void apply(View.AggregationWindow.Cumulative arg) { + if (!(windowData instanceof AggregationWindowData.CumulativeData)) { + throw new IllegalArgumentException( + "AggregationWindow and AggregationWindowData types mismatch. " + + "AggregationWindow: " + + arg + + " AggregationWindowData: " + + windowData); + } + return null; + } + }, + new Function<View.AggregationWindow.Interval, Void>() { + @Override + public Void apply(View.AggregationWindow.Interval arg) { + if (!(windowData instanceof AggregationWindowData.IntervalData)) { + throw new IllegalArgumentException( + "AggregationWindow and AggregationWindowData types mismatch. " + + "AggregationWindow: " + + arg + + " AggregationWindowData: " + + windowData); + } + return null; + } + }, + Functions.<Void>throwIllegalArgumentException()); + + Map<List<TagValue>, AggregationData> deepCopy = Maps.newHashMap(); + for (Entry<? extends List<? extends TagValue>, ? extends AggregationData> entry : map + .entrySet()) { + deepCopy.put( + Collections.unmodifiableList(new ArrayList<TagValue>(entry.getKey())), + entry.getValue()); + } + + return new AutoValue_ViewData(view, Collections.unmodifiableMap(deepCopy), windowData); + } + + /** The {@code AggregationWindowData} for a {@link ViewData}. */ + @Immutable + public abstract static class AggregationWindowData { + + private AggregationWindowData() {} + + /** Applies the given match function to the underlying data type. */ + public abstract <T> T match( + Function<? super CumulativeData, T> p0, + Function<? super IntervalData, T> p1, + Function<? super AggregationWindowData, T> defaultFunction); + + /** Cumulative {@code AggregationWindowData.} */ + @Immutable + @AutoValue + public abstract static class CumulativeData extends AggregationWindowData { + + CumulativeData() {} + + /** + * Returns the start {@code Timestamp} for a {@link CumulativeData}. + * + * @return the start {@code Timestamp}. + */ + public abstract Timestamp getStart(); + + /** + * Returns the end {@code Timestamp} for a {@link CumulativeData}. + * + * @return the end {@code Timestamp}. + */ + public abstract Timestamp getEnd(); + + @Override + public final <T> T match( + Function<? super CumulativeData, T> p0, + Function<? super IntervalData, T> p1, + Function<? super AggregationWindowData, T> defaultFunction) { + return p0.apply(this); + } + + /** Constructs a new {@link CumulativeData}. */ + public static CumulativeData create(Timestamp start, Timestamp end) { + if (start.compareTo(end) > 0) { + throw new IllegalArgumentException("Start time is later than end time."); + } + return new AutoValue_ViewData_AggregationWindowData_CumulativeData(start, end); + } + } + + /** Interval {@code AggregationWindowData.} */ + @Immutable + @AutoValue + public abstract static class IntervalData extends AggregationWindowData { + + IntervalData() {} + + /** + * Returns the end {@code Timestamp} for an {@link IntervalData}. + * + * @return the end {@code Timestamp}. + */ + public abstract Timestamp getEnd(); + + @Override + public final <T> T match( + Function<? super CumulativeData, T> p0, + Function<? super IntervalData, T> p1, + Function<? super AggregationWindowData, T> defaultFunction) { + return p1.apply(this); + } + + /** Constructs a new {@link IntervalData}. */ + public static IntervalData create(Timestamp end) { + return new AutoValue_ViewData_AggregationWindowData_IntervalData(end); + } + } + } +} diff --git a/api/src/main/java/io/opencensus/stats/ViewManager.java b/api/src/main/java/io/opencensus/stats/ViewManager.java new file mode 100644 index 00000000..16bf749e --- /dev/null +++ b/api/src/main/java/io/opencensus/stats/ViewManager.java @@ -0,0 +1,34 @@ +/* + * Copyright 2016-17, OpenCensus 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.opencensus.stats; + +/** + * Provides facilities to register {@link View}s for collecting stats and retrieving + * stats data as a {@link ViewData}. + */ +public abstract class ViewManager { + /** + * Pull model for stats. Registers a {@link View} that will collect data to be accessed + * via {@link #getView(View.Name)}. + */ + public abstract void registerView(View view); + + /** + * Returns the current stats data, {@link ViewData}, associated with the given view name. + */ + public abstract ViewData getView(View.Name view); +} diff --git a/api/src/main/java/io/opencensus/tags/InternalUtils.java b/api/src/main/java/io/opencensus/tags/InternalUtils.java new file mode 100644 index 00000000..c55ca7d4 --- /dev/null +++ b/api/src/main/java/io/opencensus/tags/InternalUtils.java @@ -0,0 +1,31 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.tags; + +import java.util.Iterator; + +/** + * Internal tagging utilities. + */ +@io.opencensus.common.Internal +public final class InternalUtils { + private InternalUtils() {} + + public static Iterator<Tag> getTags(TagContext tags) { + return tags.getIterator(); + } +} diff --git a/api/src/main/java/io/opencensus/tags/NoopTags.java b/api/src/main/java/io/opencensus/tags/NoopTags.java new file mode 100644 index 00000000..2267edb0 --- /dev/null +++ b/api/src/main/java/io/opencensus/tags/NoopTags.java @@ -0,0 +1,232 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.tags; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Preconditions; +import io.opencensus.common.Scope; +import io.opencensus.internal.NoopScope; +import io.opencensus.tags.TagKey.TagKeyBoolean; +import io.opencensus.tags.TagKey.TagKeyLong; +import io.opencensus.tags.TagKey.TagKeyString; +import io.opencensus.tags.TagValue.TagValueBoolean; +import io.opencensus.tags.TagValue.TagValueLong; +import io.opencensus.tags.TagValue.TagValueString; +import io.opencensus.tags.propagation.TagContextBinarySerializer; +import io.opencensus.tags.propagation.TagPropagationComponent; +import java.util.Collections; +import java.util.Iterator; +import javax.annotation.concurrent.Immutable; + +/** No-op implementations of tagging classes. */ +final class NoopTags { + + private NoopTags() {} + + /** + * Returns a {@code TagsComponent} that has a no-op implementation for {@link Tagger}. + * + * @return a {@code TagsComponent} that has a no-op implementation for {@code Tagger}. + */ + static TagsComponent getNoopTagsComponent() { + return NoopTagsComponent.INSTANCE; + } + + /** + * Returns a {@code Tagger} that only produces {@link TagContext}s with no tags. + * + * @return a {@code Tagger} that only produces {@code TagContext}s with no tags. + */ + static Tagger getNoopTagger() { + return NoopTagger.INSTANCE; + } + + /** + * Returns a {@code TagContextBuilder} that ignores all calls to {@link TagContextBuilder#put}. + * + * @return a {@code TagContextBuilder} that ignores all calls to {@link TagContextBuilder#put}. + */ + static TagContextBuilder getNoopTagContextBuilder() { + return NoopTagContextBuilder.INSTANCE; + } + + /** + * Returns a {@code TagContext} that does not contain any tags. + * + * @return a {@code TagContext} that does not contain any tags. + */ + static TagContext getNoopTagContext() { + return NoopTagContext.INSTANCE; + } + + /** Returns a {@code TagPropagationComponent} that contains no-op serializers. */ + static TagPropagationComponent getNoopTagPropagationComponent() { + return NoopTagPropagationComponent.INSTANCE; + } + + /** + * Returns a {@code TagContextBinarySerializer} that serializes all {@code TagContext}s to zero + * bytes and deserializes all inputs to empty {@code TagContext}s. + */ + static TagContextBinarySerializer getNoopTagContextBinarySerializer() { + return NoopTagContextBinarySerializer.INSTANCE; + } + + @Immutable + private static final class NoopTagsComponent extends TagsComponent { + static final TagsComponent INSTANCE = new NoopTagsComponent(); + + @Override + public Tagger getTagger() { + return getNoopTagger(); + } + + @Override + public TagPropagationComponent getTagPropagationComponent() { + return getNoopTagPropagationComponent(); + } + + @Override + public TaggingState getState() { + return TaggingState.DISABLED; + } + + @Override + public void setState(TaggingState state) { + Preconditions.checkNotNull(state, "state"); + } + } + + @Immutable + private static final class NoopTagger extends Tagger { + static final Tagger INSTANCE = new NoopTagger(); + + @Override + public TagContext empty() { + return getNoopTagContext(); + } + + @Override + public TagContext getCurrentTagContext() { + return getNoopTagContext(); + } + + @Override + public TagContextBuilder emptyBuilder() { + return getNoopTagContextBuilder(); + } + + @Override + public TagContextBuilder toBuilder(TagContext tags) { + checkNotNull(tags, "tags"); + return getNoopTagContextBuilder(); + } + + @Override + public TagContextBuilder currentBuilder() { + return getNoopTagContextBuilder(); + } + + @Override + public Scope withTagContext(TagContext tags) { + checkNotNull(tags, "tags"); + return NoopScope.getInstance(); + } + } + + @Immutable + private static final class NoopTagContextBuilder extends TagContextBuilder { + static final TagContextBuilder INSTANCE = new NoopTagContextBuilder(); + + @Override + public TagContextBuilder put(TagKeyString key, TagValueString value) { + checkNotNull(key, "key"); + checkNotNull(value, "value"); + return this; + } + + @Override + public TagContextBuilder put(TagKeyLong key, TagValueLong value) { + checkNotNull(key, "key"); + checkNotNull(value, "value"); + return this; + } + + @Override + public TagContextBuilder put(TagKeyBoolean key, TagValueBoolean value) { + checkNotNull(key, "key"); + checkNotNull(value, "value"); + return this; + } + + @Override + public TagContextBuilder remove(TagKey key) { + checkNotNull(key, "key"); + return this; + } + + @Override + public TagContext build() { + return getNoopTagContext(); + } + + @Override + public Scope buildScoped() { + return NoopScope.getInstance(); + } + } + + @Immutable + private static final class NoopTagContext extends TagContext { + static final TagContext INSTANCE = new NoopTagContext(); + + // TODO(sebright): Is there any way to let the user know that their tags were ignored? + @Override + protected Iterator<Tag> getIterator() { + return Collections.<Tag>emptySet().iterator(); + } + } + + @Immutable + private static final class NoopTagPropagationComponent extends TagPropagationComponent { + static final TagPropagationComponent INSTANCE = new NoopTagPropagationComponent(); + + @Override + public TagContextBinarySerializer getBinarySerializer() { + return getNoopTagContextBinarySerializer(); + } + } + + @Immutable + private static final class NoopTagContextBinarySerializer extends TagContextBinarySerializer { + static final TagContextBinarySerializer INSTANCE = new NoopTagContextBinarySerializer(); + static final byte[] EMPTY_BYTE_ARRAY = {}; + + @Override + public byte[] toByteArray(TagContext tags) { + checkNotNull(tags, "tags"); + return EMPTY_BYTE_ARRAY; + } + + @Override + public TagContext fromByteArray(byte[] bytes) { + checkNotNull(bytes, "bytes"); + return getNoopTagContext(); + } + } +} diff --git a/api/src/main/java/io/opencensus/tags/Tag.java b/api/src/main/java/io/opencensus/tags/Tag.java new file mode 100644 index 00000000..a53cd838 --- /dev/null +++ b/api/src/main/java/io/opencensus/tags/Tag.java @@ -0,0 +1,208 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.tags; + +import com.google.auto.value.AutoValue; +import io.opencensus.common.Function; +import io.opencensus.tags.TagKey.TagKeyBoolean; +import io.opencensus.tags.TagKey.TagKeyLong; +import io.opencensus.tags.TagKey.TagKeyString; +import io.opencensus.tags.TagValue.TagValueBoolean; +import io.opencensus.tags.TagValue.TagValueLong; +import io.opencensus.tags.TagValue.TagValueString; +import javax.annotation.concurrent.Immutable; + +/** {@link TagKey} paired with a value. */ +@Immutable +public abstract class Tag { + + /** + * Returns the tag's key. + * + * @return the tag's key. + */ + public abstract TagKey getKey(); + + /** + * Returns the associated tag value. + * + * @return the associated tag value. + */ + public abstract TagValue getValue(); + + Tag() {} + + /** + * Applies a function to the tag's key and value. The function that is called depends on the type + * of the tag. This is similar to the visitor pattern. {@code match} also takes a function to + * handle the default case, for backwards compatibility when tag types are added. For example, + * this code serializes a {@code Tag} and tries to handle new tag types by calling {@code + * toString()}. + * + * <pre>{@code + * byte[] serializedValue = + * tag.match( + * stringTag -> serializeString(stringTag.getValue().asString()), + * longTag -> serializeLong(longTag.getValue()), + * booleanTag -> serializeBoolean(booleanTag.getValue()), + * unknownTag -> serializeString(unknownTag.toString())); + * }</pre> + * + * <p>Without lambdas: + * + * <pre><code> + * byte[] serializedValue = + * tag.match( + * new Function<TagString, String>() { + * {@literal @}Override + * public String apply(TagString stringTag) { + * return serializeString(stringTag.getValue().asString()); + * } + * }, + * new Function<TagLong, String>() { + * {@literal @}Override + * public String apply(TagLong longTag) { + * serializeLong(longTag.getValue()); + * } + * }, + * new Function<TagBoolean, String>() { + * {@literal @}Override + * public String apply(TagBoolean booleanTag) { + * serializeBoolean(booleanTag.getValue()); + * } + * }, + * new Function<Tag, String>() { + * {@literal @}Override + * public String apply(TagBoolean unknownTag) { + * serializeString(unknownTag.toString()); + * } + * }); + * </code></pre> + * + * @param stringFunction the function to call when the tag has a {@code String} value. + * @param longFunction the function to call when the tag has a {@code long} value. + * @param booleanFunction the function to call when the tag has a {@code boolean} value. + * @param defaultFunction the function to call when the tag has a value other than {@code String}, + * {@code long}, or {@code boolean}. + * @param <T> The result type of the function. + * @return The result of calling the function that matches the tag's type. + */ + public abstract <T> T match( + Function<? super TagString, T> stringFunction, + Function<? super TagLong, T> longFunction, + Function<? super TagBoolean, T> booleanFunction, + Function<? super Tag, T> defaultFunction); + + /** A tag with a {@code String} key and value. */ + @Immutable + @AutoValue + public abstract static class TagString extends Tag { + TagString() {} + + /** + * Creates a {@code TagString} from the given {@code String} key and value. + * + * @param key the tag key. + * @param value the tag value. + * @return a {@code TagString} with the given key and value. + */ + public static TagString create(TagKeyString key, TagValueString value) { + return new AutoValue_Tag_TagString(key, value); + } + + @Override + public abstract TagKeyString getKey(); + + @Override + public abstract TagValueString getValue(); + + @Override + public final <T> T match( + Function<? super TagString, T> stringFunction, + Function<? super TagLong, T> longFunction, + Function<? super TagBoolean, T> booleanFunction, + Function<? super Tag, T> defaultFunction) { + return stringFunction.apply(this); + } + } + + /** A tag with a {@code long} key and value. */ + @Immutable + @AutoValue + public abstract static class TagLong extends Tag { + TagLong() {} + + /** + * Creates a {@code TagLong} from the given {@code long} key and value. + * + * @param key the tag key. + * @param value the tag value. + * @return a {@code TagLong} with the given key and value. + */ + public static TagLong create(TagKeyLong key, TagValueLong value) { + return new AutoValue_Tag_TagLong(key, value); + } + + @Override + public abstract TagKeyLong getKey(); + + @Override + public abstract TagValueLong getValue(); + + @Override + public final <T> T match( + Function<? super TagString, T> stringFunction, + Function<? super TagLong, T> longFunction, + Function<? super TagBoolean, T> booleanFunction, + Function<? super Tag, T> defaultFunction) { + return longFunction.apply(this); + } + } + + /** A tag with a {@code boolean} key and value. */ + @Immutable + @AutoValue + public abstract static class TagBoolean extends Tag { + TagBoolean() {} + + /** + * Creates a {@code TagBoolean} from the given {@code boolean} key and value. + * + * @param key the tag key. + * @param value the tag value. + * @return a {@code TagBoolean} with the given key and value. + */ + public static TagBoolean create(TagKeyBoolean key, TagValueBoolean value) { + return new AutoValue_Tag_TagBoolean(key, value); + } + + @Override + public abstract TagKeyBoolean getKey(); + + @Override + public abstract TagValueBoolean getValue(); + + @Override + public final <T> T match( + Function<? super TagString, T> stringFunction, + Function<? super TagLong, T> longFunction, + Function<? super TagBoolean, T> booleanFunction, + Function<? super Tag, T> defaultFunction) { + return booleanFunction.apply(this); + } + } +} diff --git a/api/src/main/java/io/opencensus/tags/TagContext.java b/api/src/main/java/io/opencensus/tags/TagContext.java new file mode 100644 index 00000000..be8f0dc1 --- /dev/null +++ b/api/src/main/java/io/opencensus/tags/TagContext.java @@ -0,0 +1,99 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.tags; + +import com.google.common.collect.HashMultiset; +import com.google.common.collect.ImmutableMultiset; +import com.google.common.collect.Lists; +import com.google.common.collect.Multiset; +import io.opencensus.tags.TagValue.TagValueString; +import java.util.Iterator; +import javax.annotation.concurrent.Immutable; + +/** + * A map from keys to values that can be used to label anything that is associated with a specific + * operation. + * + * <p>For example, {@code TagContext}s can be used to label stats, log messages, or debugging + * information. + * + * <p>Keys have type {@link TagKey}. Values have type {@link TagValueString}, though the library + * will support more types in the future, including {@code long} and {@code boolean}. + */ +@Immutable +public abstract class TagContext { + + /** + * Returns an iterator over the tags in this {@code TagContext}. + * + * @return an iterator over the tags in this {@code TagContext}. + */ + // This method is protected to prevent client code from accessing the tags of any TagContext. We + // don't currently support efficient access to tags. However, every TagContext subclass needs to + // provide access to its tags to the stats and tagging implementations by implementing this + // method. If we decide to support access to tags in the future, we can add a public iterator() + // method and implement it for all subclasses by calling getIterator(). + // + // The stats and tagging implementations can access any TagContext's tags through + // io.opencensus.tags.InternalUtils.getTags, which calls this method. + protected abstract Iterator<Tag> getIterator(); + + @Override + public String toString() { + return "TagContext"; + } + + /** + * Returns true iff the other object is an instance of {@code TagContext} and contains the same + * key-value pairs. Implementations are free to override this method to provide better + * performance. + */ + @Override + public boolean equals(Object other) { + if (!(other instanceof TagContext)) { + return false; + } + TagContext otherTags = (TagContext) other; + Iterator<Tag> iter1 = getIterator(); + Iterator<Tag> iter2 = otherTags.getIterator(); + Multiset<Tag> tags1 = + iter1 == null + ? ImmutableMultiset.<Tag>of() + : HashMultiset.create(Lists.newArrayList(iter1)); + Multiset<Tag> tags2 = + iter2 == null + ? ImmutableMultiset.<Tag>of() + : HashMultiset.create(Lists.newArrayList(iter2)); + return tags1.equals(tags2); + } + + @Override + public final int hashCode() { + int hashCode = 0; + Iterator<Tag> i = getIterator(); + if (i == null) { + return hashCode; + } + while (i.hasNext()) { + Tag tag = i.next(); + if (tag != null) { + hashCode += tag.hashCode(); + } + } + return hashCode; + } +} diff --git a/api/src/main/java/io/opencensus/tags/TagContextBuilder.java b/api/src/main/java/io/opencensus/tags/TagContextBuilder.java new file mode 100644 index 00000000..bbd6da7c --- /dev/null +++ b/api/src/main/java/io/opencensus/tags/TagContextBuilder.java @@ -0,0 +1,83 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.tags; + +import io.opencensus.common.Scope; +import io.opencensus.tags.TagKey.TagKeyBoolean; +import io.opencensus.tags.TagKey.TagKeyLong; +import io.opencensus.tags.TagKey.TagKeyString; +import io.opencensus.tags.TagValue.TagValueBoolean; +import io.opencensus.tags.TagValue.TagValueLong; +import io.opencensus.tags.TagValue.TagValueString; + +/** Builder for the {@link TagContext} class. */ +// TODO(sebright): Decide what to do when 'put' is called with a key that has the same name as an +// existing key, but a different type. We currently keep both keys. +public abstract class TagContextBuilder { + + /** + * Adds the key/value pair regardless of whether the key is present. + * + * @param key the {@code TagKey} which will be set. + * @param value the value to set for the given key. + * @return this + */ + public abstract TagContextBuilder put(TagKeyString key, TagValueString value); + + /** + * Adds the key/value pair regardless of whether the key is present. + * + * @param key the {@code TagKey} which will be set. + * @param value the value to set for the given key. + * @return this + */ + public abstract TagContextBuilder put(TagKeyLong key, TagValueLong value); + + /** + * Adds the key/value pair regardless of whether the key is present. + * + * @param key the {@code TagKey} which will be set. + * @param value the value to set for the given key. + * @return this + */ + public abstract TagContextBuilder put(TagKeyBoolean key, TagValueBoolean value); + + /** + * Removes the key if it exists. + * + * @param key the {@code TagKey} which will be removed. + * @return this + */ + public abstract TagContextBuilder remove(TagKey key); + + /** + * Creates a {@code TagContext} from this builder. + * + * @return a {@code TagContext} with the same tags as this builder. + */ + public abstract TagContext build(); + + /** + * Enters the scope of code where the {@link TagContext} created from this builder is in the + * current context and returns an object that represents that scope. The scope is exited when the + * returned object is closed. + * + * @return an object that defines a scope where the {@code TagContext} created from this builder + * is set to the current context. + */ + public abstract Scope buildScoped(); +} diff --git a/api/src/main/java/io/opencensus/tags/TagKey.java b/api/src/main/java/io/opencensus/tags/TagKey.java new file mode 100644 index 00000000..943f12d7 --- /dev/null +++ b/api/src/main/java/io/opencensus/tags/TagKey.java @@ -0,0 +1,244 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.tags; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.auto.value.AutoValue; +import io.opencensus.common.Function; +import io.opencensus.internal.StringUtil; +import io.opencensus.tags.TagValue.TagValueString; +import javax.annotation.concurrent.Immutable; + +/** + * A key to a value stored in a {@link TagContext}. + * + * <p>There is one {@code TagKey} subclass corresponding to each tag value type, so that each key + * can only be paired with a single type of value. For example, {@link TagKeyString} can only be + * used to set {@link TagValueString} values in a {@code TagContext}. + * + * <p>Each {@code TagKey} has a {@code String} name. Names have a maximum length of {@link + * #MAX_LENGTH} and contain only printable ASCII characters. + * + * <p>{@code TagKey}s are designed to be used as constants. Declaring each key as a constant ensures + * that the keys have consistent value types and prevents key names from being validated multiple + * times. + */ +@Immutable +public abstract class TagKey { + /** The maximum length for a tag key name. The value is {@value #MAX_LENGTH}. */ + public static final int MAX_LENGTH = 255; + + TagKey() {} + + public abstract String getName(); + + /** + * Applies a function to the {@code TagKey} subclass. The function that is called depends on the + * type of the tag key. This is similar to the visitor pattern. {@code match} also takes a + * function to handle the default case, for backwards compatibility when tag types are added. For + * example, this code creates a {@code Tag} from a {@code TagKey}. It handles new tag types by + * logging an error and returning a {@code TagKeyString}. + * + * <pre>{@code + * Tag tag = + * tagKey.match( + * stringKey -> TagString.create(stringKey, TagValueString.create("string value")), + * longKey -> TagLong.create(longKey, 100L), + * booleanKey -> TagBoolean.create(booleanKey, true), + * unknownKey -> { + * logger.log(Level.WARNING, "Unknown tag key type: " + unknownKey.toString()); + * return TagString.create( + * TagKeyString.create(unknownKey.getName()), + * TagValueString.create("string value")); + * }); + * }</pre> + * + * <p>Without lambdas: + * + * <pre><code> + * Tag tag = + * tagKey.match( + * new Function<TagKeyString, Tag>() { + * {@literal @}Override + * public Tag apply(TagKeyString stringKey) { + * return TagString.create(stringKey, TagValueString.create("string value")); + * } + * }, + * new Function<TagKeyLong, Tag>() { + * {@literal @}Override + * public Tag apply(TagKeyLong longKey) { + * return TagLong.create(longKey, 100L); + * } + * }, + * new Function<TagKeyBoolean, Tag>() { + * {@literal @}Override + * public Tag apply(TagKeyBoolean booleanKey) { + * return TagBoolean.create(booleanKey, true); + * } + * }, + * new Function<TagKey, Tag>() { + * {@literal @}Override + * public Tag apply(TagKey unknownKey) { + * logger.log(Level.WARNING, "Unknown tag key type: " + unknownKey.toString()); + * return TagString.create( + * TagKeyString.create(unknownKey.getName()), + * TagValueString.create("string value")); + * } + * }); + * </code></pre> + * + * @param stringFunction the function to call when the {@code TagKey} is a {@code TagKeyString}. + * @param longFunction the function to call when the {@code TagKey} is a {@code TagKeyLong}. + * @param booleanFunction the function to call when the {@code TagKey} is a {@code TagKeyBoolean}. + * @param defaultFunction the function to call when the tag key has a type other than {@code + * String}, {@code long}, or {@code boolean}. + * @param <T> The result type of the function. + * @return The result of calling the function that matches the tag key's type. + */ + // TODO(sebright): Should we make this public in the first release? + public abstract <T> T match( + Function<? super TagKeyString, T> stringFunction, + Function<? super TagKeyLong, T> longFunction, + Function<? super TagKeyBoolean, T> booleanFunction, + Function<? super TagKey, T> defaultFunction); + + /** + * Determines whether the given {@code String} is a valid tag key. + * + * @param name the tag key name to be validated. + * @return whether the name is valid. + */ + private static boolean isValid(String name) { + return name.length() <= MAX_LENGTH && StringUtil.isPrintableString(name); + } + + /** A {@code TagKey} for values of type {@code String}. */ + @Immutable + @AutoValue + public abstract static class TagKeyString extends TagKey { + + /** + * Constructs a {@code TagKeyString} with the given name. + * + * <p>The name must meet the following requirements: + * + * <ol> + * <li>It cannot be longer than {@link #MAX_LENGTH}. + * <li>It can only contain printable ASCII characters. + * </ol> + * + * @param name the name of the key. + * @return a {@code TagKeyString} with the given name. + * @throws IllegalArgumentException if the name is not valid. + */ + public static TagKeyString create(String name) { + // TODO(sebright): Should we disallow an empty name? + checkArgument(isValid(name)); + return new AutoValue_TagKey_TagKeyString(name); + } + + @Override + public final <T> T match( + Function<? super TagKeyString, T> stringFunction, + Function<? super TagKeyLong, T> longFunction, + Function<? super TagKeyBoolean, T> booleanFunction, + Function<? super TagKey, T> defaultFunction) { + return stringFunction.apply(this); + } + } + + /** + * A {@code TagKey} for values of type {@code long}. + * + * <p>Note that {@link TagKeyLong} isn't supported by the implementation yet, so the factory + * method isn't exposed. + */ + @Immutable + @AutoValue + public abstract static class TagKeyLong extends TagKey { + + /** + * Constructs a {@code TagKeyLong} with the given name. + * + * <p>The name must meet the following requirements: + * + * <ol> + * <li>It cannot be longer than {@link #MAX_LENGTH}. + * <li>It can only contain printable ASCII characters. + * </ol> + * + * @param name the name of the key. + * @return a {@code TagKeyLong} with the given name. + * @throws IllegalArgumentException if the name is not valid. + */ + // TODO(sebright): Make this public once we support types other than String. + static TagKeyLong create(String name) { + checkArgument(isValid(name)); + return new AutoValue_TagKey_TagKeyLong(name); + } + + @Override + public final <T> T match( + Function<? super TagKeyString, T> stringFunction, + Function<? super TagKeyLong, T> longFunction, + Function<? super TagKeyBoolean, T> booleanFunction, + Function<? super TagKey, T> defaultFunction) { + return longFunction.apply(this); + } + } + + /** + * A {@code TagKey} for values of type {@code boolean}. + * + * <p>Note that {@link TagKeyBoolean} isn't supported by the implementation yet, so the factory + * method isn't exposed. + */ + @Immutable + @AutoValue + public abstract static class TagKeyBoolean extends TagKey { + + /** + * Constructs a {@code TagKeyBoolean} with the given name. + * + * <p>The name must meet the following requirements: + * + * <ol> + * <li>It cannot be longer than {@link #MAX_LENGTH}. + * <li>It can only contain printable ASCII characters. + * </ol> + * + * @param name the name of the key. + * @return a {@code TagKeyBoolean} with the given name. + * @throws IllegalArgumentException if the name is not valid. + */ + // TODO(sebright): Make this public once we support types other than String. + static TagKeyBoolean create(String name) { + checkArgument(isValid(name)); + return new AutoValue_TagKey_TagKeyBoolean(name); + } + + @Override + public final <T> T match( + Function<? super TagKeyString, T> stringFunction, + Function<? super TagKeyLong, T> longFunction, + Function<? super TagKeyBoolean, T> booleanFunction, + Function<? super TagKey, T> defaultFunction) { + return booleanFunction.apply(this); + } + } +} diff --git a/api/src/main/java/io/opencensus/tags/TagValue.java b/api/src/main/java/io/opencensus/tags/TagValue.java new file mode 100644 index 00000000..7da713c5 --- /dev/null +++ b/api/src/main/java/io/opencensus/tags/TagValue.java @@ -0,0 +1,182 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.tags; + +import com.google.auto.value.AutoValue; +import com.google.common.base.Preconditions; +import io.opencensus.common.Function; +import io.opencensus.internal.StringUtil; +import io.opencensus.tags.TagKey.TagKeyBoolean; +import io.opencensus.tags.TagKey.TagKeyLong; +import io.opencensus.tags.TagKey.TagKeyString; +import javax.annotation.concurrent.Immutable; + +/** A validated tag value. */ +@Immutable +public abstract class TagValue { + + TagValue() {} + + /** + * Applies a function to a tag value. The function that is called depends on the type of the + * value. This is similar to the visitor pattern. {@code match} also takes a function to handle + * the default case, for backwards compatibility when tag types are added. + * + * @param stringFunction the function to call when the tag value has type {@code String}. + * @param longFunction the function to call when the tag value has type {@code long}. + * @param booleanFunction the function to call when the tag value has type {@code boolean}. + * @param defaultFunction the function to call when the tag value has a type other than {@code + * String}, {@code long}, or {@code boolean}. + * @param <T> The result type of the function. + * @return The result of calling the function that matches the tag value's type. + */ + public abstract <T> T match( + Function<? super TagValueString, T> stringFunction, + Function<? super TagValueLong, T> longFunction, + Function<? super TagValueBoolean, T> booleanFunction, + Function<? super TagValue, T> defaultFunction); + + /** + * A validated tag value associated with a {@link TagKeyString}. + * + * <p>Validation ensures that the {@code String} has a maximum length of {@link #MAX_LENGTH} and + * contains only printable ASCII characters. + */ + @Immutable + @AutoValue + public abstract static class TagValueString extends TagValue { + /** The maximum length for a {@code String} tag value. The value is {@value #MAX_LENGTH}. */ + public static final int MAX_LENGTH = 256; + + TagValueString() {} + + /** + * Constructs a {@code TagValueString} from the given string. The string must meet the following + * requirements: + * + * <ol> + * <li>It cannot be longer than {@link #MAX_LENGTH}. + * <li>It can only contain printable ASCII characters. + * </ol> + * + * @param value the tag value. + * @throws IllegalArgumentException if the {@code String} is not valid. + */ + public static TagValueString create(String value) { + Preconditions.checkArgument(isValid(value)); + return new AutoValue_TagValue_TagValueString(value); + } + + @Override + public final <T> T match( + Function<? super TagValueString, T> stringFunction, + Function<? super TagValueLong, T> longFunction, + Function<? super TagValueBoolean, T> booleanFunction, + Function<? super TagValue, T> defaultFunction) { + return stringFunction.apply(this); + } + + /** + * Returns the tag value as a {@code String}. + * + * @return the tag value as a {@code String}. + */ + public abstract String asString(); + + /** + * Determines whether the given {@code String} is a valid tag value. + * + * @param value the tag value to be validated. + * @return whether the value is valid. + */ + private static boolean isValid(String value) { + return value.length() <= MAX_LENGTH && StringUtil.isPrintableString(value); + } + } + + /** A tag value associated with a {@link TagKeyLong}. */ + @Immutable + @AutoValue + public abstract static class TagValueLong extends TagValue { + + TagValueLong() {} + + /** + * Constructs a {@code TagValueLong} from the given {@code long}. + * + * @param value the tag value. + */ + public static TagValueLong create(long value) { + return new AutoValue_TagValue_TagValueLong(value); + } + + @Override + public final <T> T match( + Function<? super TagValueString, T> stringFunction, + Function<? super TagValueLong, T> longFunction, + Function<? super TagValueBoolean, T> booleanFunction, + Function<? super TagValue, T> defaultFunction) { + return longFunction.apply(this); + } + + /** + * Returns the tag value as a {@code long}. + * + * @return the tag value as a {@code long}. + */ + public abstract long asLong(); + } + + /** A tag value associated with a {@link TagKeyBoolean}. */ + @Immutable + @AutoValue + public abstract static class TagValueBoolean extends TagValue { + private static final TagValueBoolean TRUE_VALUE = createInternal(true); + private static final TagValueBoolean FALSE_VALUE = createInternal(false); + + TagValueBoolean() {} + + /** + * Constructs a {@code TagValueBoolean} from the given {@code boolean}. + * + * @param value the tag value. + */ + public static TagValueBoolean create(boolean value) { + return value ? TRUE_VALUE : FALSE_VALUE; + } + + private static TagValueBoolean createInternal(boolean value) { + return new AutoValue_TagValue_TagValueBoolean(value); + } + + @Override + public final <T> T match( + Function<? super TagValueString, T> stringFunction, + Function<? super TagValueLong, T> longFunction, + Function<? super TagValueBoolean, T> booleanFunction, + Function<? super TagValue, T> defaultFunction) { + return booleanFunction.apply(this); + } + + /** + * Returns the tag value as a {@code boolean}. + * + * @return the tag value as a {@code boolean}. + */ + public abstract boolean asBoolean(); + } +} diff --git a/api/src/main/java/io/opencensus/tags/Tagger.java b/api/src/main/java/io/opencensus/tags/Tagger.java new file mode 100644 index 00000000..1d786f71 --- /dev/null +++ b/api/src/main/java/io/opencensus/tags/Tagger.java @@ -0,0 +1,78 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.tags; + +import io.opencensus.common.Scope; + +/** + * Object for creating new {@link TagContext}s and {@code TagContext}s based on the current context. + * + * <p>This class returns {@link TagContextBuilder builders} that can be used to create the + * implementation-dependent {@link TagContext}s. + * + * <p>Implementations may have different constraints and are free to convert tag contexts to their + * own subtypes. This means callers cannot assume the {@link #getCurrentTagContext() current + * context} is the same instance as the one {@link #withTagContext(TagContext) placed into scope}. + */ +public abstract class Tagger { + + /** + * Returns an empty {@code TagContext}. + * + * @return an empty {@code TagContext}. + */ + public abstract TagContext empty(); + + /** + * Returns the current {@code TagContext}. + * + * @return the current {@code TagContext}. + */ + public abstract TagContext getCurrentTagContext(); + + /** + * Returns a new empty {@code Builder}. + * + * @return a new empty {@code Builder}. + */ + public abstract TagContextBuilder emptyBuilder(); + + /** + * Returns a builder based on this {@code TagContext}. + * + * @return a builder based on this {@code TagContext}. + */ + public abstract TagContextBuilder toBuilder(TagContext tags); + + /** + * Returns a new builder created from the current {@code TagContext}. + * + * @return a new builder created from the current {@code TagContext}. + */ + public abstract TagContextBuilder currentBuilder(); + + /** + * Enters the scope of code where the given {@code TagContext} is in the current context and + * returns an object that represents that scope. The scope is exited when the returned object is + * closed. + * + * @param tags the {@code TagContext} to be set to the current context. + * @return an object that defines a scope where the given {@code TagContext} is set to the current + * context. + */ + public abstract Scope withTagContext(TagContext tags); +} diff --git a/api/src/main/java/io/opencensus/tags/TaggingState.java b/api/src/main/java/io/opencensus/tags/TaggingState.java new file mode 100644 index 00000000..861aca74 --- /dev/null +++ b/api/src/main/java/io/opencensus/tags/TaggingState.java @@ -0,0 +1,40 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.tags; + +/** State of the {@link TagsComponent}. */ +public enum TaggingState { + // TODO(sebright): Should we add a state that propagates the tags, but doesn't allow + // modifications? + + /** + * State that fully enables tagging. + * + * <p>The {@link TagsComponent} can add tags to {@link TagContext}s, propagate {@code TagContext}s + * in the current context, and serialize {@code TagContext}s. + */ + ENABLED, + + /** + * State that disables tagging. + * + * <p>The {@link TagsComponent} may not add tags to {@link TagContext}s, propagate {@code + * TagContext}s in the current context, or serialize {@code TagContext}s. + */ + // TODO(sebright): Document how this interacts with stats collection. + DISABLED +} diff --git a/api/src/main/java/io/opencensus/tags/Tags.java b/api/src/main/java/io/opencensus/tags/Tags.java new file mode 100644 index 00000000..5378fa5a --- /dev/null +++ b/api/src/main/java/io/opencensus/tags/Tags.java @@ -0,0 +1,104 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.tags; + +import com.google.common.annotations.VisibleForTesting; +import io.opencensus.internal.Provider; +import io.opencensus.tags.propagation.TagPropagationComponent; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** Class for accessing the default {@link TagsComponent}. */ +public final class Tags { + private static final Logger logger = Logger.getLogger(Tags.class.getName()); + + private static final TagsComponent tagsComponent = + loadTagsComponent(TagsComponent.class.getClassLoader()); + + private Tags() {} + + /** + * Returns the default {@code Tagger}. + * + * @return the default {@code Tagger}. + */ + public static Tagger getTagger() { + return tagsComponent.getTagger(); + } + + /** + * Returns the default {@code TagPropagationComponent}. + * + * @return the default {@code TagPropagationComponent}. + */ + public static TagPropagationComponent getTagPropagationComponent() { + return tagsComponent.getTagPropagationComponent(); + } + + /** + * Returns the current {@code TaggingState}. + * + * <p>When no implementation is available, {@code getState} always returns {@link + * TaggingState#DISABLED}. + * + * @return the current {@code TaggingState}. + */ + public static TaggingState getState() { + return tagsComponent.getState(); + } + + /** + * Sets the current {@code TaggingState}. + * + * <p>When no implementation is available, {@code setState} has no effect. + * + * @param state the new {@code TaggingState}. + */ + public static void setState(TaggingState state) { + tagsComponent.setState(state); + } + + // Any provider that may be used for TagsComponent can be added here. + @VisibleForTesting + static TagsComponent loadTagsComponent(ClassLoader classLoader) { + try { + // Call Class.forName with literal string name of the class to help shading tools. + return Provider.createInstance( + Class.forName("io.opencensus.impl.tags.TagsComponentImpl", true, classLoader), + TagsComponent.class); + } catch (ClassNotFoundException e) { + logger.log( + Level.FINE, + "Couldn't load full implementation for TagsComponent, now trying to load lite " + + "implementation.", + e); + } + try { + // Call Class.forName with literal string name of the class to help shading tools. + return Provider.createInstance( + Class.forName("io.opencensus.impllite.tags.TagsComponentImplLite", true, classLoader), + TagsComponent.class); + } catch (ClassNotFoundException e) { + logger.log( + Level.FINE, + "Couldn't load lite implementation for TagsComponent, now using " + + "default implementation for TagsComponent.", + e); + } + return NoopTags.getNoopTagsComponent(); + } +} diff --git a/api/src/main/java/io/opencensus/tags/TagsComponent.java b/api/src/main/java/io/opencensus/tags/TagsComponent.java new file mode 100644 index 00000000..755173d8 --- /dev/null +++ b/api/src/main/java/io/opencensus/tags/TagsComponent.java @@ -0,0 +1,52 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.tags; + +import io.opencensus.tags.propagation.TagPropagationComponent; + +/** + * Class that holds the implementation for {@link Tagger} and {@link TagPropagationComponent}. + * + * <p>All objects returned by methods on {@code TagsComponent} are cacheable. + */ +public abstract class TagsComponent { + + /** Returns the {@link Tagger} for this implementation. */ + public abstract Tagger getTagger(); + + /** Returns the {@link TagPropagationComponent} for this implementation. */ + public abstract TagPropagationComponent getTagPropagationComponent(); + + /** + * Returns the current {@code TaggingState}. + * + * <p>When no implementation is available, {@code getState} always returns {@link + * TaggingState#DISABLED}. + * + * @return the current {@code TaggingState}. + */ + public abstract TaggingState getState(); + + /** + * Sets the current {@code TaggingState}. + * + * <p>When no implementation is available, {@code setState} has no effect. + * + * @param state the new {@code TaggingState}. + */ + public abstract void setState(TaggingState state); +} diff --git a/api/src/main/java/io/opencensus/tags/package-info.java b/api/src/main/java/io/opencensus/tags/package-info.java new file mode 100644 index 00000000..2a332f6d --- /dev/null +++ b/api/src/main/java/io/opencensus/tags/package-info.java @@ -0,0 +1,33 @@ +/* + * Copyright 2017, OpenCensus 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. + */ + +/** + * API for associating tags with scoped operations. + * + * <p>This package manages a set of tags in the {@code io.grpc.Context}. The tags can be used to + * label anything that is associated with a specific operation. For example, the {@code + * io.opencensus.stats} package labels all stats with the current tags. + * + * <p>{@link io.opencensus.tags.Tag Tags} are key-value pairs. The {@link io.opencensus.tags.TagKey + * keys} are wrapped {@code String}s, but the values can have multiple types, such as {@code + * String}, {@code long}, and {@code boolean}. They are stored as a map in a {@link + * io.opencensus.tags.TagContext}. + * + * <p>Note that tags are independent of the tracing data that is propagated in the {@code + * io.grpc.Context}, such as trace ID. + */ +// TODO(sebright): Add code examples after the API is updated to use a TagContext factory. +package io.opencensus.tags; diff --git a/api/src/main/java/io/opencensus/tags/propagation/TagContextBinarySerializer.java b/api/src/main/java/io/opencensus/tags/propagation/TagContextBinarySerializer.java new file mode 100644 index 00000000..e54e5d3d --- /dev/null +++ b/api/src/main/java/io/opencensus/tags/propagation/TagContextBinarySerializer.java @@ -0,0 +1,51 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.tags.propagation; + +import io.opencensus.tags.TagContext; + +/** + * Object for serializing and deserializing {@link TagContext}s with the binary format. + * + * <p>See <a + * href="https://github.com/census-instrumentation/opencensus-specs/blob/master/encodings/BinaryEncoding.md#tag-context">opencensus-specs</a> + * for the specification of the cross-language binary serialization format. + */ +public abstract class TagContextBinarySerializer { + + /** + * Serializes the {@code TagContext} into the on-the-wire representation. + * + * <p>This method should be the inverse of {@link #fromByteArray}. + * + * @param tags the {@code TagContext} to serialize. + * @return the on-the-wire representation of a {@code TagContext}. + */ + public abstract byte[] toByteArray(TagContext tags); + + /** + * Creates a {@code TagContext} from the given on-the-wire encoded representation. + * + * <p>This method should be the inverse of {@link #toByteArray}. + * + * @param bytes on-the-wire representation of a {@code TagContext}. + * @return a {@code TagContext} deserialized from {@code bytes}. + * @throws TagContextParseException if there is a parse error or the serialized {@code TagContext} + * contains invalid tags. + */ + public abstract TagContext fromByteArray(byte[] bytes) throws TagContextParseException; +} diff --git a/api/src/main/java/io/opencensus/tags/propagation/TagContextParseException.java b/api/src/main/java/io/opencensus/tags/propagation/TagContextParseException.java new file mode 100644 index 00000000..0174c416 --- /dev/null +++ b/api/src/main/java/io/opencensus/tags/propagation/TagContextParseException.java @@ -0,0 +1,43 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.tags.propagation; + +import io.opencensus.tags.TagContext; + +/** Exception thrown when a {@link TagContext} cannot be parsed. */ +public final class TagContextParseException extends Exception { + private static final long serialVersionUID = 0L; + + /** + * Constructs a new {@code TagContextParseException} with the given message. + * + * @param message a message describing the parse error. + */ + public TagContextParseException(String message) { + super(message); + } + + /** + * Constructs a new {@code TagContextParseException} with the given message and cause. + * + * @param message a message describing the parse error. + * @param cause the cause of the parse error. + */ + public TagContextParseException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/api/src/main/java/io/opencensus/tags/propagation/TagPropagationComponent.java b/api/src/main/java/io/opencensus/tags/propagation/TagPropagationComponent.java new file mode 100644 index 00000000..c51a845c --- /dev/null +++ b/api/src/main/java/io/opencensus/tags/propagation/TagPropagationComponent.java @@ -0,0 +1,31 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.tags.propagation; + +import io.opencensus.tags.TagContext; + +/** Object containing all supported {@link TagContext} propagation formats. */ +// TODO(sebright): Add an HTTP serializer. +public abstract class TagPropagationComponent { + + /** + * Returns the {@link TagContextBinarySerializer} for this implementation. + * + * @return the {@code TagContextBinarySerializer} for this implementation. + */ + public abstract TagContextBinarySerializer getBinarySerializer(); +} diff --git a/api/src/main/java/io/opencensus/tags/unsafe/ContextUtils.java b/api/src/main/java/io/opencensus/tags/unsafe/ContextUtils.java new file mode 100644 index 00000000..2baf1a2a --- /dev/null +++ b/api/src/main/java/io/opencensus/tags/unsafe/ContextUtils.java @@ -0,0 +1,52 @@ +/* + * Copyright 2017, OpenCensus 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.opencensus.tags.unsafe; + +import io.grpc.Context; +import io.opencensus.tags.Tag; +import io.opencensus.tags.TagContext; +import java.util.Collections; +import java.util.Iterator; +import javax.annotation.concurrent.Immutable; + +/** + * Utility methods for accessing the {@link TagContext} contained in the {@link io.grpc.Context}. + * + * <p>Most code should interact with the current context via the public APIs in {@link + * io.opencensus.tags.TagContext} and avoid accessing {@link #TAG_CONTEXT_KEY} directly. + */ +public final class ContextUtils { + private static final TagContext EMPTY_TAG_CONTEXT = new EmptyTagContext(); + + private ContextUtils() {} + + /** + * The {@link io.grpc.Context.Key} used to interact with the {@code TagContext} contained in the + * {@link io.grpc.Context}. + */ + public static final Context.Key<TagContext> TAG_CONTEXT_KEY = + Context.keyWithDefault("opencensus-tag-context-key", EMPTY_TAG_CONTEXT); + + @Immutable + private static final class EmptyTagContext extends TagContext { + + @Override + protected Iterator<Tag> getIterator() { + return Collections.<Tag>emptySet().iterator(); + } + } +} |