aboutsummaryrefslogtreecommitdiff
path: root/api/src/main/java/io/opencensus/tags
diff options
context:
space:
mode:
authorJulien Desprez <jdesprez@google.com>2018-10-22 11:42:32 -0700
committerandroid-build-merger <android-build-merger@google.com>2018-10-22 11:42:32 -0700
commit108d816db7cf4559963e6474a679af7a25d892ad (patch)
treeede84fcf0a9687d4907ae5f8a4788271d62e0922 /api/src/main/java/io/opencensus/tags
parentcfbefd32336596ea63784607e4106dc37ce0567f (diff)
parent13217871fefa43f6d16fbb31b04e9904996d87d5 (diff)
downloadopencensus-java-108d816db7cf4559963e6474a679af7a25d892ad.tar.gz
Merge remote-tracking branch 'aosp/upstream-master' into merge am: dd3cabeacc am: 6fbc3cf5a1
am: 13217871fe Change-Id: Icfe12d6db2f81a6f5c65241919b1d63fb665ffd6
Diffstat (limited to 'api/src/main/java/io/opencensus/tags')
-rw-r--r--api/src/main/java/io/opencensus/tags/InternalUtils.java38
-rw-r--r--api/src/main/java/io/opencensus/tags/NoopTags.java214
-rw-r--r--api/src/main/java/io/opencensus/tags/Tag.java60
-rw-r--r--api/src/main/java/io/opencensus/tags/TagContext.java109
-rw-r--r--api/src/main/java/io/opencensus/tags/TagContextBuilder.java65
-rw-r--r--api/src/main/java/io/opencensus/tags/TagKey.java84
-rw-r--r--api/src/main/java/io/opencensus/tags/TagValue.java79
-rw-r--r--api/src/main/java/io/opencensus/tags/Tagger.java86
-rw-r--r--api/src/main/java/io/opencensus/tags/TaggingState.java48
-rw-r--r--api/src/main/java/io/opencensus/tags/Tags.java126
-rw-r--r--api/src/main/java/io/opencensus/tags/TagsComponent.java73
-rw-r--r--api/src/main/java/io/opencensus/tags/package-info.java32
-rw-r--r--api/src/main/java/io/opencensus/tags/propagation/TagContextBinarySerializer.java57
-rw-r--r--api/src/main/java/io/opencensus/tags/propagation/TagContextDeserializationException.java49
-rw-r--r--api/src/main/java/io/opencensus/tags/propagation/TagContextSerializationException.java49
-rw-r--r--api/src/main/java/io/opencensus/tags/propagation/TagPropagationComponent.java36
-rw-r--r--api/src/main/java/io/opencensus/tags/unsafe/ContextUtils.java56
17 files changed, 1261 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..944122e1
--- /dev/null
+++ b/api/src/main/java/io/opencensus/tags/InternalUtils.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ *
+ * @since 0.8
+ */
+@io.opencensus.common.Internal
+public final class InternalUtils {
+ private InternalUtils() {}
+
+ /**
+ * Internal tag accessor.
+ *
+ * @since 0.8
+ */
+ 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..fb52b164
--- /dev/null
+++ b/api/src/main/java/io/opencensus/tags/NoopTags.java
@@ -0,0 +1,214 @@
+/*
+ * 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.internal.NoopScope;
+import io.opencensus.internal.Utils;
+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;
+import javax.annotation.concurrent.ThreadSafe;
+
+/** 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 newNoopTagsComponent() {
+ return new NoopTagsComponent();
+ }
+
+ /**
+ * 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;
+ }
+
+ @ThreadSafe
+ private static final class NoopTagsComponent extends TagsComponent {
+ private volatile boolean isRead;
+
+ @Override
+ public Tagger getTagger() {
+ return getNoopTagger();
+ }
+
+ @Override
+ public TagPropagationComponent getTagPropagationComponent() {
+ return getNoopTagPropagationComponent();
+ }
+
+ @Override
+ public TaggingState getState() {
+ isRead = true;
+ return TaggingState.DISABLED;
+ }
+
+ @Override
+ @Deprecated
+ public void setState(TaggingState state) {
+ Utils.checkNotNull(state, "state");
+ Utils.checkState(!isRead, "State was already read, cannot set 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) {
+ Utils.checkNotNull(tags, "tags");
+ return getNoopTagContextBuilder();
+ }
+
+ @Override
+ public TagContextBuilder currentBuilder() {
+ return getNoopTagContextBuilder();
+ }
+
+ @Override
+ public Scope withTagContext(TagContext tags) {
+ Utils.checkNotNull(tags, "tags");
+ return NoopScope.getInstance();
+ }
+ }
+
+ @Immutable
+ private static final class NoopTagContextBuilder extends TagContextBuilder {
+ static final TagContextBuilder INSTANCE = new NoopTagContextBuilder();
+
+ @Override
+ public TagContextBuilder put(TagKey key, TagValue value) {
+ Utils.checkNotNull(key, "key");
+ Utils.checkNotNull(value, "value");
+ return this;
+ }
+
+ @Override
+ public TagContextBuilder remove(TagKey key) {
+ Utils.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) {
+ Utils.checkNotNull(tags, "tags");
+ return EMPTY_BYTE_ARRAY;
+ }
+
+ @Override
+ public TagContext fromByteArray(byte[] bytes) {
+ Utils.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..9e0a7a82
--- /dev/null
+++ b/api/src/main/java/io/opencensus/tags/Tag.java
@@ -0,0 +1,60 @@
+/*
+ * 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 javax.annotation.concurrent.Immutable;
+
+/**
+ * {@link TagKey} paired with a {@link TagValue}.
+ *
+ * @since 0.8
+ */
+@Immutable
+@AutoValue
+public abstract class Tag {
+
+ Tag() {}
+
+ /**
+ * Creates a {@code Tag} from the given key and value.
+ *
+ * @param key the tag key.
+ * @param value the tag value.
+ * @return a {@code Tag} with the given key and value.
+ * @since 0.8
+ */
+ public static Tag create(TagKey key, TagValue value) {
+ return new AutoValue_Tag(key, value);
+ }
+
+ /**
+ * Returns the tag's key.
+ *
+ * @return the tag's key.
+ * @since 0.8
+ */
+ public abstract TagKey getKey();
+
+ /**
+ * Returns the tag's value.
+ *
+ * @return the tag's value.
+ * @since 0.8
+ */
+ public abstract TagValue getValue();
+}
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..e36acdff
--- /dev/null
+++ b/api/src/main/java/io/opencensus/tags/TagContext.java
@@ -0,0 +1,109 @@
+/*
+ * 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.HashMap;
+import java.util.Iterator;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * A map from {@link TagKey} to {@link TagValue} 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.
+ *
+ * @since 0.8
+ */
+@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}.
+ * @since 0.8
+ */
+ // 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(@Nullable Object other) {
+ if (!(other instanceof TagContext)) {
+ return false;
+ }
+ TagContext otherTags = (TagContext) other;
+ Iterator<Tag> iter1 = getIterator();
+ Iterator<Tag> iter2 = otherTags.getIterator();
+ HashMap<Tag, Integer> tags = new HashMap<Tag, Integer>();
+ while (iter1 != null && iter1.hasNext()) {
+ Tag tag = iter1.next();
+ if (tags.containsKey(tag)) {
+ tags.put(tag, tags.get(tag) + 1);
+ } else {
+ tags.put(tag, 1);
+ }
+ }
+ while (iter2 != null && iter2.hasNext()) {
+ Tag tag = iter2.next();
+ if (!tags.containsKey(tag)) {
+ return false;
+ }
+ int count = tags.get(tag);
+ if (count > 1) {
+ tags.put(tag, count - 1);
+ } else {
+ tags.remove(tag);
+ }
+ }
+ return tags.isEmpty();
+ }
+
+ @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..f4268968
--- /dev/null
+++ b/api/src/main/java/io/opencensus/tags/TagContextBuilder.java
@@ -0,0 +1,65 @@
+/*
+ * 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;
+
+/**
+ * Builder for the {@link TagContext} class.
+ *
+ * @since 0.8
+ */
+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 {@code TagValue} to set for the given key.
+ * @return this
+ * @since 0.8
+ */
+ public abstract TagContextBuilder put(TagKey key, TagValue value);
+
+ /**
+ * Removes the key if it exists.
+ *
+ * @param key the {@code TagKey} which will be removed.
+ * @return this
+ * @since 0.8
+ */
+ public abstract TagContextBuilder remove(TagKey key);
+
+ /**
+ * Creates a {@code TagContext} from this builder.
+ *
+ * @return a {@code TagContext} with the same tags as this builder.
+ * @since 0.8
+ */
+ 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.
+ * @since 0.8
+ */
+ 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..ca4582bd
--- /dev/null
+++ b/api/src/main/java/io/opencensus/tags/TagKey.java
@@ -0,0 +1,84 @@
+/*
+ * 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.internal.StringUtils;
+import io.opencensus.internal.Utils;
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * A key to a value stored in a {@link 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
+ * prevents key names from being validated multiple times.
+ *
+ * @since 0.8
+ */
+@Immutable
+@AutoValue
+public abstract class TagKey {
+ /**
+ * The maximum length for a tag key name. The value is {@value #MAX_LENGTH}.
+ *
+ * @since 0.8
+ */
+ public static final int MAX_LENGTH = 255;
+
+ TagKey() {}
+
+ /**
+ * Constructs a {@code TagKey} 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 TagKey} with the given name.
+ * @throws IllegalArgumentException if the name is not valid.
+ * @since 0.8
+ */
+ public static TagKey create(String name) {
+ Utils.checkArgument(isValid(name), "Invalid TagKey name: %s", name);
+ return new AutoValue_TagKey(name);
+ }
+
+ /**
+ * Returns the name of the key.
+ *
+ * @return the name of the key.
+ * @since 0.8
+ */
+ public abstract String getName();
+
+ /**
+ * 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.isEmpty() && name.length() <= MAX_LENGTH && StringUtils.isPrintableString(name);
+ }
+}
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..9111ca28
--- /dev/null
+++ b/api/src/main/java/io/opencensus/tags/TagValue.java
@@ -0,0 +1,79 @@
+/*
+ * 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.internal.StringUtils;
+import io.opencensus.internal.Utils;
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * A validated tag value.
+ *
+ * <p>Validation ensures that the {@code String} has a maximum length of {@link #MAX_LENGTH} and
+ * contains only printable ASCII characters.
+ *
+ * @since 0.8
+ */
+@Immutable
+@AutoValue
+public abstract class TagValue {
+ /**
+ * The maximum length for a tag value. The value is {@value #MAX_LENGTH}.
+ *
+ * @since 0.8
+ */
+ public static final int MAX_LENGTH = 255;
+
+ TagValue() {}
+
+ /**
+ * Constructs a {@code TagValue} 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.
+ * @since 0.8
+ */
+ public static TagValue create(String value) {
+ Utils.checkArgument(isValid(value), "Invalid TagValue: %s", value);
+ return new AutoValue_TagValue(value);
+ }
+
+ /**
+ * Returns the tag value as a {@code String}.
+ *
+ * @return the tag value as a {@code String}.
+ * @since 0.8
+ */
+ 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 && StringUtils.isPrintableString(value);
+ }
+}
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..f1e203ad
--- /dev/null
+++ b/api/src/main/java/io/opencensus/tags/Tagger.java
@@ -0,0 +1,86 @@
+/*
+ * 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}.
+ *
+ * @since 0.8
+ */
+public abstract class Tagger {
+
+ /**
+ * Returns an empty {@code TagContext}.
+ *
+ * @return an empty {@code TagContext}.
+ * @since 0.8
+ */
+ public abstract TagContext empty();
+
+ /**
+ * Returns the current {@code TagContext}.
+ *
+ * @return the current {@code TagContext}.
+ * @since 0.8
+ */
+ public abstract TagContext getCurrentTagContext();
+
+ /**
+ * Returns a new empty {@code Builder}.
+ *
+ * @return a new empty {@code Builder}.
+ * @since 0.8
+ */
+ public abstract TagContextBuilder emptyBuilder();
+
+ /**
+ * Returns a builder based on this {@code TagContext}.
+ *
+ * @return a builder based on this {@code TagContext}.
+ * @since 0.8
+ */
+ 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}.
+ * @since 0.8
+ */
+ public abstract TagContextBuilder currentBuilder();
+
+ /**
+ * Enters the scope of code where the given {@code TagContext} is in the current context
+ * (replacing the previous {@code TagContext}) 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.
+ * @since 0.8
+ */
+ 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..88970361
--- /dev/null
+++ b/api/src/main/java/io/opencensus/tags/TaggingState.java
@@ -0,0 +1,48 @@
+/*
+ * 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}.
+ *
+ * @since 0.8
+ */
+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.
+ *
+ * @since 0.8
+ */
+ 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.
+ *
+ * @since 0.8
+ */
+ // 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..07123647
--- /dev/null
+++ b/api/src/main/java/io/opencensus/tags/Tags.java
@@ -0,0 +1,126 @@
+/*
+ * 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.internal.DefaultVisibilityForTesting;
+import io.opencensus.internal.Provider;
+import io.opencensus.tags.propagation.TagPropagationComponent;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.annotation.Nullable;
+
+/**
+ * Class for accessing the default {@link TagsComponent}.
+ *
+ * @since 0.8
+ */
+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}.
+ * @since 0.8
+ */
+ public static Tagger getTagger() {
+ return tagsComponent.getTagger();
+ }
+
+ /**
+ * Returns the default {@code TagPropagationComponent}.
+ *
+ * @return the default {@code TagPropagationComponent}.
+ * @since 0.8
+ */
+ 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}.
+ *
+ * <p>Once {@link #getState()} is called, subsequent calls to {@link #setState(TaggingState)} will
+ * throw an {@code IllegalStateException}.
+ *
+ * @return the current {@code TaggingState}.
+ * @since 0.8
+ */
+ public static TaggingState getState() {
+ return tagsComponent.getState();
+ }
+
+ /**
+ * Sets the current {@code TaggingState}.
+ *
+ * <p>When no implementation is available, {@code setState} does not change the state.
+ *
+ * @param state the new {@code TaggingState}.
+ * @throws IllegalStateException if {@link #getState()} was previously called.
+ * @deprecated This method is deprecated because other libraries could cache the result of {@link
+ * #getState()}, use a stale value, and behave incorrectly. It is only safe to call early in
+ * initialization. This method throws {@link IllegalStateException} after {@link #getState()}
+ * has been called, in order to limit changes to the result of {@code getState()}.
+ * @since 0.8
+ */
+ @Deprecated
+ public static void setState(TaggingState state) {
+ tagsComponent.setState(state);
+ }
+
+ // Any provider that may be used for TagsComponent can be added here.
+ @DefaultVisibilityForTesting
+ static TagsComponent loadTagsComponent(@Nullable 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", /*initialize=*/ 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",
+ /*initialize=*/ 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.newNoopTagsComponent();
+ }
+}
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..d34f1951
--- /dev/null
+++ b/api/src/main/java/io/opencensus/tags/TagsComponent.java
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ *
+ * @since 0.8
+ */
+public abstract class TagsComponent {
+
+ /**
+ * Returns the {@link Tagger} for this implementation.
+ *
+ * @since 0.8
+ */
+ public abstract Tagger getTagger();
+
+ /**
+ * Returns the {@link TagPropagationComponent} for this implementation.
+ *
+ * @since 0.8
+ */
+ public abstract TagPropagationComponent getTagPropagationComponent();
+
+ /**
+ * Returns the current {@code TaggingState}.
+ *
+ * <p>When no implementation is available, {@code getState} always returns {@link
+ * TaggingState#DISABLED}.
+ *
+ * <p>Once {@link #getState()} is called, subsequent calls to {@link #setState(TaggingState)} will
+ * throw an {@code IllegalStateException}.
+ *
+ * @return the current {@code TaggingState}.
+ * @since 0.8
+ */
+ public abstract TaggingState getState();
+
+ /**
+ * Sets the current {@code TaggingState}.
+ *
+ * <p>When no implementation is available, {@code setState} does not change the state.
+ *
+ * @param state the new {@code TaggingState}.
+ * @throws IllegalStateException if {@link #getState()} was previously called.
+ * @deprecated This method is deprecated because other libraries could cache the result of {@link
+ * #getState()}, use a stale value, and behave incorrectly. It is only safe to call early in
+ * initialization. This method throws {@link IllegalStateException} after {@code getState()}
+ * has been called, in order to limit changes to the result of {@code getState()}.
+ * @since 0.8
+ */
+ @Deprecated
+ 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..eb19ee77
--- /dev/null
+++ b/api/src/main/java/io/opencensus/tags/package-info.java
@@ -0,0 +1,32 @@
+/*
+ * 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} and {@link io.opencensus.tags.TagValue values} are wrapped {@code String}s. 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.
+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..39eb8cee
--- /dev/null
+++ b/api/src/main/java/io/opencensus/tags/propagation/TagContextBinarySerializer.java
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ *
+ * @since 0.8
+ */
+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}.
+ * @throws TagContextSerializationException if the result would be larger than the maximum allowed
+ * serialized size.
+ * @since 0.8
+ */
+ public abstract byte[] toByteArray(TagContext tags) throws TagContextSerializationException;
+
+ /**
+ * 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 TagContextDeserializationException if there is a parse error, the input contains
+ * invalid tags, or the input is larger than the maximum allowed serialized size.
+ * @since 0.8
+ */
+ public abstract TagContext fromByteArray(byte[] bytes) throws TagContextDeserializationException;
+}
diff --git a/api/src/main/java/io/opencensus/tags/propagation/TagContextDeserializationException.java b/api/src/main/java/io/opencensus/tags/propagation/TagContextDeserializationException.java
new file mode 100644
index 00000000..11dcb59f
--- /dev/null
+++ b/api/src/main/java/io/opencensus/tags/propagation/TagContextDeserializationException.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ *
+ * @since 0.8
+ */
+public final class TagContextDeserializationException extends Exception {
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * Constructs a new {@code TagContextParseException} with the given message.
+ *
+ * @param message a message describing the error.
+ * @since 0.8
+ */
+ public TagContextDeserializationException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new {@code TagContextParseException} with the given message and cause.
+ *
+ * @param message a message describing the error.
+ * @param cause the cause of the error.
+ * @since 0.8
+ */
+ public TagContextDeserializationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/api/src/main/java/io/opencensus/tags/propagation/TagContextSerializationException.java b/api/src/main/java/io/opencensus/tags/propagation/TagContextSerializationException.java
new file mode 100644
index 00000000..bb3c9b74
--- /dev/null
+++ b/api/src/main/java/io/opencensus/tags/propagation/TagContextSerializationException.java
@@ -0,0 +1,49 @@
+/*
+ * 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 serialized.
+ *
+ * @since 0.8
+ */
+public final class TagContextSerializationException extends Exception {
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * Constructs a new {@code TagContextSerializationException} with the given message.
+ *
+ * @param message a message describing the error.
+ * @since 0.8
+ */
+ public TagContextSerializationException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new {@code TagContextSerializationException} with the given message and cause.
+ *
+ * @param message a message describing the error.
+ * @param cause the cause of the error.
+ * @since 0.8
+ */
+ public TagContextSerializationException(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..6ececa79
--- /dev/null
+++ b/api/src/main/java/io/opencensus/tags/propagation/TagPropagationComponent.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.tags.propagation;
+
+import io.opencensus.tags.TagContext;
+
+/**
+ * Object containing all supported {@link TagContext} propagation formats.
+ *
+ * @since 0.8
+ */
+// TODO(sebright): Add an HTTP serializer.
+public abstract class TagPropagationComponent {
+
+ /**
+ * Returns the {@link TagContextBinarySerializer} for this implementation.
+ *
+ * @return the {@code TagContextBinarySerializer} for this implementation.
+ * @since 0.8
+ */
+ 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..8936bbbb
--- /dev/null
+++ b/api/src/main/java/io/opencensus/tags/unsafe/ContextUtils.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ *
+ * @since 0.8
+ */
+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}.
+ *
+ * @since 0.8
+ */
+ 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();
+ }
+ }
+}