1 /* 2 * Copyright 2017, OpenCensus Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.opencensus.trace.propagation; 18 19 import io.opencensus.common.ExperimentalApi; 20 import io.opencensus.internal.Utils; 21 import io.opencensus.trace.SpanContext; 22 import java.util.Collections; 23 import java.util.List; 24 import javax.annotation.Nullable; 25 26 /*>>> 27 import org.checkerframework.checker.nullness.qual.NonNull; 28 */ 29 30 /** 31 * Injects and extracts {@link SpanContext trace identifiers} as text into carriers that travel 32 * in-band across process boundaries. Identifiers are often encoded as messaging or RPC request 33 * headers. 34 * 35 * <p>When using http, the carrier of propagated data on both the client (injector) and server 36 * (extractor) side is usually an http request. Propagation is usually implemented via library- 37 * specific request interceptors, where the client-side injects span identifiers and the server-side 38 * extracts them. 39 * 40 * <p>Example of usage on the client: 41 * 42 * <pre>{@code 43 * private static final Tracer tracer = Tracing.getTracer(); 44 * private static final TextFormat textFormat = Tracing.getPropagationComponent().getTextFormat(); 45 * private static final TextFormat.Setter setter = new TextFormat.Setter<HttpURLConnection>() { 46 * public void put(HttpURLConnection carrier, String key, String value) { 47 * carrier.setRequestProperty(field, value); 48 * } 49 * } 50 * 51 * void makeHttpRequest() { 52 * Span span = tracer.spanBuilder("Sent.MyRequest").startSpan(); 53 * try (Scope s = tracer.withSpan(span)) { 54 * HttpURLConnection connection = 55 * (HttpURLConnection) new URL("http://myserver").openConnection(); 56 * textFormat.inject(span.getContext(), connection, httpURLConnectionSetter); 57 * // Send the request, wait for response and maybe set the status if not ok. 58 * } 59 * span.end(); // Can set a status. 60 * } 61 * }</pre> 62 * 63 * <p>Example of usage on the server: 64 * 65 * <pre>{@code 66 * private static final Tracer tracer = Tracing.getTracer(); 67 * private static final TextFormat textFormat = Tracing.getPropagationComponent().getTextFormat(); 68 * private static final TextFormat.Getter<HttpRequest> getter = ...; 69 * 70 * void onRequestReceived(HttpRequest request) { 71 * SpanContext spanContext = textFormat.extract(request, getter); 72 * Span span = tracer.spanBuilderWithRemoteParent("Recv.MyRequest", spanContext).startSpan(); 73 * try (Scope s = tracer.withSpan(span)) { 74 * // Handle request and send response back. 75 * } 76 * span.end() 77 * } 78 * }</pre> 79 * 80 * @since 0.11 81 */ 82 @ExperimentalApi 83 public abstract class TextFormat { 84 private static final NoopTextFormat NOOP_TEXT_FORMAT = new NoopTextFormat(); 85 86 /** 87 * The propagation fields defined. If your carrier is reused, you should delete the fields here 88 * before calling {@link #inject(SpanContext, Object, Setter)}. 89 * 90 * <p>For example, if the carrier is a single-use or immutable request object, you don't need to 91 * clear fields as they couldn't have been set before. If it is a mutable, retryable object, 92 * successive calls should clear these fields first. 93 * 94 * @since 0.11 95 */ 96 // The use cases of this are: 97 // * allow pre-allocation of fields, especially in systems like gRPC Metadata 98 // * allow a single-pass over an iterator (ex OpenTracing has no getter in TextMap) fields()99 public abstract List<String> fields(); 100 101 /** 102 * Injects the span context downstream. For example, as http headers. 103 * 104 * @param spanContext possibly not sampled. 105 * @param carrier holds propagation fields. For example, an outgoing message or http request. 106 * @param setter invoked for each propagation key to add or remove. 107 * @since 0.11 108 */ inject( SpanContext spanContext, C carrier, Setter<C> setter)109 public abstract <C /*>>> extends @NonNull Object*/> void inject( 110 SpanContext spanContext, C carrier, Setter<C> setter); 111 112 /** 113 * Class that allows a {@code TextFormat} to set propagated fields into a carrier. 114 * 115 * <p>{@code Setter} is stateless and allows to be saved as a constant to avoid runtime 116 * allocations. 117 * 118 * @param <C> carrier of propagation fields, such as an http request 119 * @since 0.11 120 */ 121 public abstract static class Setter<C> { 122 123 /** 124 * Replaces a propagated field with the given value. 125 * 126 * <p>For example, a setter for an {@link java.net.HttpURLConnection} would be the method 127 * reference {@link java.net.HttpURLConnection#addRequestProperty(String, String)} 128 * 129 * @param carrier holds propagation fields. For example, an outgoing message or http request. 130 * @param key the key of the field. 131 * @param value the value of the field. 132 * @since 0.11 133 */ put(C carrier, String key, String value)134 public abstract void put(C carrier, String key, String value); 135 } 136 137 /** 138 * Extracts the span context from upstream. For example, as http headers. 139 * 140 * @param carrier holds propagation fields. For example, an outgoing message or http request. 141 * @param getter invoked for each propagation key to get. 142 * @throws SpanContextParseException if the input is invalid 143 * @since 0.11 144 */ extract( C carrier, Getter<C> getter)145 public abstract <C /*>>> extends @NonNull Object*/> SpanContext extract( 146 C carrier, Getter<C> getter) throws SpanContextParseException; 147 148 /** 149 * Class that allows a {@code TextFormat} to read propagated fields from a carrier. 150 * 151 * <p>{@code Getter} is stateless and allows to be saved as a constant to avoid runtime 152 * allocations. 153 * 154 * @param <C> carrier of propagation fields, such as an http request 155 * @since 0.11 156 */ 157 public abstract static class Getter<C> { 158 159 /** 160 * Returns the first value of the given propagation {@code key} or returns {@code null}. 161 * 162 * @param carrier carrier of propagation fields, such as an http request 163 * @param key the key of the field. 164 * @return the first value of the given propagation {@code key} or returns {@code null}. 165 * @since 0.11 166 */ 167 @Nullable get(C carrier, String key)168 public abstract String get(C carrier, String key); 169 } 170 171 /** 172 * Returns the no-op implementation of the {@code TextFormat}. 173 * 174 * @return the no-op implementation of the {@code TextFormat}. 175 */ getNoopTextFormat()176 static TextFormat getNoopTextFormat() { 177 return NOOP_TEXT_FORMAT; 178 } 179 180 private static final class NoopTextFormat extends TextFormat { 181 NoopTextFormat()182 private NoopTextFormat() {} 183 184 @Override fields()185 public List<String> fields() { 186 return Collections.emptyList(); 187 } 188 189 @Override inject( SpanContext spanContext, C carrier, Setter<C> setter)190 public <C /*>>> extends @NonNull Object*/> void inject( 191 SpanContext spanContext, C carrier, Setter<C> setter) { 192 Utils.checkNotNull(spanContext, "spanContext"); 193 Utils.checkNotNull(carrier, "carrier"); 194 Utils.checkNotNull(setter, "setter"); 195 } 196 197 @Override extract(C carrier, Getter<C> getter)198 public <C /*>>> extends @NonNull Object*/> SpanContext extract(C carrier, Getter<C> getter) { 199 Utils.checkNotNull(carrier, "carrier"); 200 Utils.checkNotNull(getter, "getter"); 201 return SpanContext.INVALID; 202 } 203 } 204 } 205