diff options
| author | Yang Song <songy23@users.noreply.github.com> | 2019-04-10 12:26:22 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-04-10 12:26:22 -0700 |
| commit | 44906aae202aee946ec7f994f32077dacf13ef05 (patch) | |
| tree | 14e2d65e377285a845dd7302a2835b44bcc9274e /api | |
| parent | 8564d8146a315370ccd1c7ccf3d98adc9c128098 (diff) | |
| download | platform_external_opencensus-java-44906aae202aee946ec7f994f32077dacf13ef05.tar.gz platform_external_opencensus-java-44906aae202aee946ec7f994f32077dacf13ef05.tar.bz2 platform_external_opencensus-java-44906aae202aee946ec7f994f32077dacf13ef05.zip | |
Tags: Add HTTP text serializer API. (#1800)
* Tags: Add HTTP text serializer API.
* Add setter/getter and inject/extract to support encoding tags to multiple strings.
* Import checker framework annotation.
* Update changelog
Diffstat (limited to 'api')
4 files changed, 300 insertions, 1 deletions
diff --git a/api/src/main/java/io/opencensus/tags/NoopTags.java b/api/src/main/java/io/opencensus/tags/NoopTags.java index 4d1d3b8d..c4baadb5 100644 --- a/api/src/main/java/io/opencensus/tags/NoopTags.java +++ b/api/src/main/java/io/opencensus/tags/NoopTags.java @@ -20,12 +20,20 @@ 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.TagContextDeserializationException; +import io.opencensus.tags.propagation.TagContextSerializationException; +import io.opencensus.tags.propagation.TagContextTextFormat; import io.opencensus.tags.propagation.TagPropagationComponent; import java.util.Collections; import java.util.Iterator; +import java.util.List; import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.ThreadSafe; +/*>>> +import org.checkerframework.checker.nullness.qual.NonNull; +*/ + /** No-op implementations of tagging classes. */ final class NoopTags { @@ -80,8 +88,17 @@ final class NoopTags { return NoopTagContextBinarySerializer.INSTANCE; } + /** + * Returns a {@code TagContextTextFormat} that serializes all {@code TagContext}s to empty strings + * and deserializes all inputs to empty {@code TagContext}s. + */ + static TagContextTextFormat getNoopTagContextTextSerializer() { + return NoopTagContextTextFormat.INSTANCE; + } + @ThreadSafe private static final class NoopTagsComponent extends TagsComponent { + private volatile boolean isRead; @Override @@ -110,6 +127,7 @@ final class NoopTags { @Immutable private static final class NoopTagger extends Tagger { + static final Tagger INSTANCE = new NoopTagger(); @Override @@ -147,6 +165,7 @@ final class NoopTags { @Immutable private static final class NoopTagContextBuilder extends TagContextBuilder { + static final TagContextBuilder INSTANCE = new NoopTagContextBuilder(); @Override @@ -184,6 +203,7 @@ final class NoopTags { @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? @@ -195,16 +215,23 @@ final class NoopTags { @Immutable private static final class NoopTagPropagationComponent extends TagPropagationComponent { + static final TagPropagationComponent INSTANCE = new NoopTagPropagationComponent(); @Override public TagContextBinarySerializer getBinarySerializer() { return getNoopTagContextBinarySerializer(); } + + @Override + public TagContextTextFormat getCorrelationContextFormat() { + return getNoopTagContextTextSerializer(); + } } @Immutable private static final class NoopTagContextBinarySerializer extends TagContextBinarySerializer { + static final TagContextBinarySerializer INSTANCE = new NoopTagContextBinarySerializer(); static final byte[] EMPTY_BYTE_ARRAY = {}; @@ -220,4 +247,32 @@ final class NoopTags { return getNoopTagContext(); } } + + @Immutable + private static final class NoopTagContextTextFormat extends TagContextTextFormat { + + static final NoopTagContextTextFormat INSTANCE = new NoopTagContextTextFormat(); + + @Override + public List<String> fields() { + return Collections.<String>emptyList(); + } + + @Override + public <C /*>>> extends @NonNull Object*/> void inject( + TagContext tagContext, C carrier, Setter<C> setter) + throws TagContextSerializationException { + Utils.checkNotNull(tagContext, "tagContext"); + Utils.checkNotNull(carrier, "carrier"); + Utils.checkNotNull(setter, "setter"); + } + + @Override + public <C /*>>> extends @NonNull Object*/> TagContext extract(C carrier, Getter<C> getter) + throws TagContextDeserializationException { + Utils.checkNotNull(carrier, "carrier"); + Utils.checkNotNull(getter, "getter"); + return getNoopTagContext(); + } + } } diff --git a/api/src/main/java/io/opencensus/tags/propagation/TagContextTextFormat.java b/api/src/main/java/io/opencensus/tags/propagation/TagContextTextFormat.java new file mode 100644 index 00000000..af22827e --- /dev/null +++ b/api/src/main/java/io/opencensus/tags/propagation/TagContextTextFormat.java @@ -0,0 +1,165 @@ +/* + * Copyright 2019, 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; +import java.util.List; +import javax.annotation.Nullable; + +/*>>> +import org.checkerframework.checker.nullness.qual.NonNull; +*/ + +/** + * Object for injecting and extracting {@link TagContext} as text into carriers that travel in-band + * across process boundaries. Tags are often encoded as messaging or RPC request headers. + * + * <p>When using http, the carrier of propagated data on both the client (injector) and server + * (extractor) side is usually an http request. Propagation is usually implemented via library- + * specific request interceptors, where the client-side injects tags and the server-side extracts + * them. + * + * <p>Example of usage on the client: + * + * <pre>{@code + * private static final Tagger tagger = Tags.getTagger(); + * private static final TagContextTextFormat textFormat = + * Tags.getPropagationComponent().getCorrelationContextFormat(); + * private static final TagContextTextFormat.Setter setter = + * new TagContextTextFormat.Setter<HttpURLConnection>() { + * public void put(HttpURLConnection carrier, String key, String value) { + * carrier.setRequestProperty(field, value); + * } + * }; + * + * void makeHttpRequest() { + * TagContext tagContext = tagger.emptyBuilder().put(K, V).build(); + * try (Scope s = tagger.withTagContext(tagContext)) { + * HttpURLConnection connection = + * (HttpURLConnection) new URL("http://myserver").openConnection(); + * textFormat.inject(tagContext, connection, httpURLConnectionSetter); + * // Send the request, wait for response and maybe set the status if not ok. + * } + * } + * }</pre> + * + * <p>Example of usage on the server: + * + * <pre>{@code + * private static final Tagger tagger = Tags.getTagger(); + * private static final TagContextTextFormat textFormat = + * Tags.getPropagationComponent().getCorrelationContextFormat(); + * private static final TagContextTextFormat.Getter<HttpRequest> getter = ...; + * + * void onRequestReceived(HttpRequest request) { + * TagContext tagContext = textFormat.extract(request, getter); + * try (Scope s = tagger.withTagContext(tagContext)) { + * // Handle request and send response back. + * } + * } + * }</pre> + * + * @since 0.21 + */ +public abstract class TagContextTextFormat { + + /** + * The propagation fields defined. If your carrier is reused, you should delete the fields here + * before calling {@link #inject(TagContext, Object, Setter)}. + * + * <p>For example, if the carrier is a single-use or immutable request object, you don't need to + * clear fields as they couldn't have been set before. If it is a mutable, retryable object, + * successive calls should clear these fields first. + * + * @since 0.21 + */ + // The use cases of this are: + // * allow pre-allocation of fields, especially in systems like gRPC Metadata + // * allow a single-pass over an iterator (ex OpenTracing has no getter in TextMap) + public abstract List<String> fields(); + + /** + * Injects the tag context downstream. For example, as http headers. + * + * @param tagContext the tag context. + * @param carrier holds propagation fields. For example, an outgoing message or http request. + * @param setter invoked for each propagation key to add or remove. + * @throws TagContextSerializationException if the given tag context cannot be serialized. + * @since 0.21 + */ + public abstract <C /*>>> extends @NonNull Object*/> void inject( + TagContext tagContext, C carrier, Setter<C> setter) throws TagContextSerializationException; + + /** + * Class that allows a {@code TagContextTextFormat} to set propagated fields into a carrier. + * + * <p>{@code Setter} is stateless and allows to be saved as a constant to avoid runtime + * allocations. + * + * @param <C> carrier of propagation fields, such as an http request + * @since 0.21 + */ + public abstract static class Setter<C> { + + /** + * Replaces a propagated field with the given value. + * + * <p>For example, a setter for an {@link java.net.HttpURLConnection} would be the method + * reference {@link java.net.HttpURLConnection#addRequestProperty(String, String)} + * + * @param carrier holds propagation fields. For example, an outgoing message or http request. + * @param key the key of the field. + * @param value the value of the field. + * @since 0.21 + */ + public abstract void put(C carrier, String key, String value); + } + + /** + * Extracts the tag context from upstream. For example, as http headers. + * + * @param carrier holds propagation fields. For example, an outgoing message or http request. + * @param getter invoked for each propagation key to get. + * @throws TagContextDeserializationException if the input is invalid + * @since 0.21 + */ + public abstract <C /*>>> extends @NonNull Object*/> TagContext extract( + C carrier, Getter<C> getter) throws TagContextDeserializationException; + + /** + * Class that allows a {@code TagContextTextFormat} to read propagated fields from a carrier. + * + * <p>{@code Getter} is stateless and allows to be saved as a constant to avoid runtime + * allocations. + * + * @param <C> carrier of propagation fields, such as an http request + * @since 0.21 + */ + public abstract static class Getter<C> { + + /** + * Returns the first value of the given propagation {@code key} or returns {@code null}. + * + * @param carrier carrier of propagation fields, such as an http request + * @param key the key of the field. + * @return the first value of the given propagation {@code key} or returns {@code null}. + * @since 0.21 + */ + @Nullable + public abstract String get(C carrier, String key); + } +} diff --git a/api/src/main/java/io/opencensus/tags/propagation/TagPropagationComponent.java b/api/src/main/java/io/opencensus/tags/propagation/TagPropagationComponent.java index 6ececa79..1c4d0ca5 100644 --- a/api/src/main/java/io/opencensus/tags/propagation/TagPropagationComponent.java +++ b/api/src/main/java/io/opencensus/tags/propagation/TagPropagationComponent.java @@ -23,7 +23,6 @@ import io.opencensus.tags.TagContext; * * @since 0.8 */ -// TODO(sebright): Add an HTTP serializer. public abstract class TagPropagationComponent { /** @@ -33,4 +32,15 @@ public abstract class TagPropagationComponent { * @since 0.8 */ public abstract TagContextBinarySerializer getBinarySerializer(); + + /** + * Returns the {@link TagContextTextFormat} for this implementation. + * + * <p>OpenCensus uses W3C Correlation Context as the HTTP text format. For more details, see <a + * href="https://github.com/w3c/correlation-context">correlation-context</a>. + * + * @return the {@code TagContextTextFormat} for this implementation. + * @since 0.21 + */ + public abstract TagContextTextFormat getCorrelationContextFormat(); } diff --git a/api/src/test/java/io/opencensus/tags/NoopTagsTest.java b/api/src/test/java/io/opencensus/tags/NoopTagsTest.java index e7c1ebfd..574af15a 100644 --- a/api/src/test/java/io/opencensus/tags/NoopTagsTest.java +++ b/api/src/test/java/io/opencensus/tags/NoopTagsTest.java @@ -23,8 +23,12 @@ import io.opencensus.internal.NoopScope; import io.opencensus.tags.propagation.TagContextBinarySerializer; import io.opencensus.tags.propagation.TagContextDeserializationException; import io.opencensus.tags.propagation.TagContextSerializationException; +import io.opencensus.tags.propagation.TagContextTextFormat; +import io.opencensus.tags.propagation.TagContextTextFormat.Getter; +import io.opencensus.tags.propagation.TagContextTextFormat.Setter; import java.util.Arrays; import java.util.Iterator; +import javax.annotation.Nullable; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -46,6 +50,21 @@ public final class NoopTagsTest { } }; + private static final Setter<Object> NOOP_SETTER = + new Setter<Object>() { + @Override + public void put(Object carrier, String key, String value) {} + }; + + private static final Getter<Object> NOOP_GETTER = + new Getter<Object>() { + @Nullable + @Override + public String get(Object carrier, String key) { + return null; + } + }; + @Rule public final ExpectedException thrown = ExpectedException.none(); @Test @@ -157,6 +176,8 @@ public final class NoopTagsTest { public void noopTagPropagationComponent() { assertThat(NoopTags.getNoopTagPropagationComponent().getBinarySerializer()) .isSameAs(NoopTags.getNoopTagContextBinarySerializer()); + assertThat(NoopTags.getNoopTagPropagationComponent().getCorrelationContextFormat()) + .isSameAs(NoopTags.getNoopTagContextTextSerializer()); } @Test @@ -183,4 +204,52 @@ public final class NoopTagsTest { thrown.expect(NullPointerException.class); noopSerializer.fromByteArray(null); } + + @Test + public void noopTagContextTextFormat() + throws TagContextDeserializationException, TagContextSerializationException { + NoopTags.getNoopTagContextTextSerializer().inject(TAG_CONTEXT, new Object(), NOOP_SETTER); + assertThat(NoopTags.getNoopTagContextTextSerializer().extract(new Object(), NOOP_GETTER)) + .isEqualTo(NoopTags.getNoopTagContext()); + } + + @Test + public void noopTagContextTextFormat_inject_DisallowsNullTagContext() + throws TagContextSerializationException { + TagContextTextFormat noopSerializer = NoopTags.getNoopTagContextTextSerializer(); + thrown.expect(NullPointerException.class); + noopSerializer.inject(null, new Object(), NOOP_SETTER); + } + + @Test + public void noopTagContextTextFormat_inject_DisallowsNullCarrier() + throws TagContextSerializationException { + TagContextTextFormat noopSerializer = NoopTags.getNoopTagContextTextSerializer(); + thrown.expect(NullPointerException.class); + noopSerializer.inject(TAG_CONTEXT, null, NOOP_SETTER); + } + + @Test + public void noopTagContextTextFormat_inject_DisallowsNullSetter() + throws TagContextSerializationException { + TagContextTextFormat noopSerializer = NoopTags.getNoopTagContextTextSerializer(); + thrown.expect(NullPointerException.class); + noopSerializer.inject(TAG_CONTEXT, new Object(), null); + } + + @Test + public void noopTagContextTextFormat_extract_DisallowsNullCarrier() + throws TagContextDeserializationException { + TagContextTextFormat noopSerializer = NoopTags.getNoopTagContextTextSerializer(); + thrown.expect(NullPointerException.class); + noopSerializer.extract(null, NOOP_GETTER); + } + + @Test + public void noopTagContextTextFormat_extract_DisallowsNullGetter() + throws TagContextDeserializationException { + TagContextTextFormat noopSerializer = NoopTags.getNoopTagContextTextSerializer(); + thrown.expect(NullPointerException.class); + noopSerializer.extract(new Object(), null); + } } |
