aboutsummaryrefslogtreecommitdiff
path: root/api/src/main/java/io/opencensus/tags
diff options
context:
space:
mode:
authorsebright <sebright@google.com>2017-10-23 17:13:14 -0700
committerBogdan Drutu <bdrutu@google.com>2017-10-23 17:13:14 -0700
commit92e363fb2daa1a8aee308d3bd5fc20c9e83eeab2 (patch)
tree34cf4a254947a69b24ed522f997d15c072bfe8e4 /api/src/main/java/io/opencensus/tags
parente69bb99d61c529fc9ad8c0be83d1f438c93a63af (diff)
downloadopencensus-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/opencensus/tags')
-rw-r--r--api/src/main/java/io/opencensus/tags/InternalUtils.java31
-rw-r--r--api/src/main/java/io/opencensus/tags/NoopTags.java232
-rw-r--r--api/src/main/java/io/opencensus/tags/Tag.java208
-rw-r--r--api/src/main/java/io/opencensus/tags/TagContext.java99
-rw-r--r--api/src/main/java/io/opencensus/tags/TagContextBuilder.java83
-rw-r--r--api/src/main/java/io/opencensus/tags/TagKey.java244
-rw-r--r--api/src/main/java/io/opencensus/tags/TagValue.java182
-rw-r--r--api/src/main/java/io/opencensus/tags/Tagger.java78
-rw-r--r--api/src/main/java/io/opencensus/tags/TaggingState.java40
-rw-r--r--api/src/main/java/io/opencensus/tags/Tags.java104
-rw-r--r--api/src/main/java/io/opencensus/tags/TagsComponent.java52
-rw-r--r--api/src/main/java/io/opencensus/tags/package-info.java33
-rw-r--r--api/src/main/java/io/opencensus/tags/propagation/TagContextBinarySerializer.java51
-rw-r--r--api/src/main/java/io/opencensus/tags/propagation/TagContextParseException.java43
-rw-r--r--api/src/main/java/io/opencensus/tags/propagation/TagPropagationComponent.java31
-rw-r--r--api/src/main/java/io/opencensus/tags/unsafe/ContextUtils.java52
16 files changed, 1563 insertions, 0 deletions
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&lt;TagString, String&gt;() {
+ * {@literal @}Override
+ * public String apply(TagString stringTag) {
+ * return serializeString(stringTag.getValue().asString());
+ * }
+ * },
+ * new Function&lt;TagLong, String&gt;() {
+ * {@literal @}Override
+ * public String apply(TagLong longTag) {
+ * serializeLong(longTag.getValue());
+ * }
+ * },
+ * new Function&lt;TagBoolean, String&gt;() {
+ * {@literal @}Override
+ * public String apply(TagBoolean booleanTag) {
+ * serializeBoolean(booleanTag.getValue());
+ * }
+ * },
+ * new Function&lt;Tag, String&gt;() {
+ * {@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&lt;TagKeyString, Tag&gt;() {
+ * {@literal @}Override
+ * public Tag apply(TagKeyString stringKey) {
+ * return TagString.create(stringKey, TagValueString.create("string value"));
+ * }
+ * },
+ * new Function&lt;TagKeyLong, Tag&gt;() {
+ * {@literal @}Override
+ * public Tag apply(TagKeyLong longKey) {
+ * return TagLong.create(longKey, 100L);
+ * }
+ * },
+ * new Function&lt;TagKeyBoolean, Tag&gt;() {
+ * {@literal @}Override
+ * public Tag apply(TagKeyBoolean booleanKey) {
+ * return TagBoolean.create(booleanKey, true);
+ * }
+ * },
+ * new Function&lt;TagKey, Tag&gt;() {
+ * {@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();
+ }
+ }
+}