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; 18 19 import io.opencensus.internal.DefaultVisibilityForTesting; 20 import io.opencensus.internal.Utils; 21 import java.util.Arrays; 22 import javax.annotation.Nullable; 23 import javax.annotation.concurrent.Immutable; 24 25 /** 26 * A class that represents global trace options. These options are propagated to all child {@link 27 * io.opencensus.trace.Span spans}. These determine features such as whether a {@code Span} should 28 * be traced. It is implemented as a bitmask. 29 * 30 * @since 0.5 31 */ 32 @Immutable 33 public final class TraceOptions { 34 // Default options. Nothing set. 35 private static final byte DEFAULT_OPTIONS = 0; 36 // Bit to represent whether trace is sampled or not. 37 private static final byte IS_SAMPLED = 0x1; 38 39 /** 40 * The size in bytes of the {@code TraceOptions}. 41 * 42 * @since 0.5 43 */ 44 public static final int SIZE = 1; 45 46 private static final int BASE16_SIZE = 2 * SIZE; 47 48 /** 49 * The default {@code TraceOptions}. 50 * 51 * @since 0.5 52 */ 53 public static final TraceOptions DEFAULT = fromByte(DEFAULT_OPTIONS); 54 55 // The set of enabled features is determined by all the enabled bits. 56 private final byte options; 57 58 // Creates a new {@code TraceOptions} with the given options. TraceOptions(byte options)59 private TraceOptions(byte options) { 60 this.options = options; 61 } 62 63 /** 64 * Returns a {@code TraceOptions} built from a byte representation. 65 * 66 * <p>Equivalent with: 67 * 68 * <pre>{@code 69 * TraceOptions.fromBytes(buffer, 0); 70 * }</pre> 71 * 72 * @param buffer the representation of the {@code TraceOptions}. 73 * @return a {@code TraceOptions} whose representation is given by the {@code buffer} parameter. 74 * @throws NullPointerException if {@code buffer} is null. 75 * @throws IllegalArgumentException if {@code buffer.length} is not {@link TraceOptions#SIZE}. 76 * @since 0.5 77 * @deprecated use {@link #fromByte(byte)}. 78 */ 79 @Deprecated fromBytes(byte[] buffer)80 public static TraceOptions fromBytes(byte[] buffer) { 81 Utils.checkNotNull(buffer, "buffer"); 82 Utils.checkArgument( 83 buffer.length == SIZE, "Invalid size: expected %s, got %s", SIZE, buffer.length); 84 return fromByte(buffer[0]); 85 } 86 87 /** 88 * Returns a {@code TraceOptions} whose representation is copied from the {@code src} beginning at 89 * the {@code srcOffset} offset. 90 * 91 * @param src the buffer where the representation of the {@code TraceOptions} is copied. 92 * @param srcOffset the offset in the buffer where the representation of the {@code TraceOptions} 93 * begins. 94 * @return a {@code TraceOptions} whose representation is copied from the buffer. 95 * @throws NullPointerException if {@code src} is null. 96 * @throws IndexOutOfBoundsException if {@code srcOffset+TraceOptions.SIZE} is greater than {@code 97 * src.length}. 98 * @since 0.5 99 * @deprecated use {@link #fromByte(byte)}. 100 */ 101 @Deprecated fromBytes(byte[] src, int srcOffset)102 public static TraceOptions fromBytes(byte[] src, int srcOffset) { 103 Utils.checkIndex(srcOffset, src.length); 104 return fromByte(src[srcOffset]); 105 } 106 107 /** 108 * Returns a {@code TraceOptions} whose representation is {@code src}. 109 * 110 * @param src the byte representation of the {@code TraceOptions}. 111 * @return a {@code TraceOptions} whose representation is {@code src}. 112 * @since 0.16 113 */ fromByte(byte src)114 public static TraceOptions fromByte(byte src) { 115 // TODO(bdrutu): OPTIMIZATION: Cache all the 256 possible objects and return from the cache. 116 return new TraceOptions(src); 117 } 118 119 /** 120 * Returns a {@code TraceOption} built from a lowercase base16 representation. 121 * 122 * @param src the lowercase base16 representation. 123 * @param srcOffset the offset in the buffer where the representation of the {@code TraceOptions} 124 * begins. 125 * @return a {@code TraceOption} built from a lowercase base16 representation. 126 * @throws NullPointerException if {@code src} is null. 127 * @throws IllegalArgumentException if {@code src.length} is not {@code 2 * TraceOption.SIZE} OR 128 * if the {@code str} has invalid characters. 129 * @since 0.18 130 */ fromLowerBase16(CharSequence src, int srcOffset)131 public static TraceOptions fromLowerBase16(CharSequence src, int srcOffset) { 132 return new TraceOptions(BigendianEncoding.byteFromBase16String(src, srcOffset)); 133 } 134 135 /** 136 * Returns the one byte representation of the {@code TraceOptions}. 137 * 138 * @return the one byte representation of the {@code TraceOptions}. 139 * @since 0.16 140 */ getByte()141 public byte getByte() { 142 return options; 143 } 144 145 /** 146 * Returns the 1-byte array representation of the {@code TraceOptions}. 147 * 148 * @return the 1-byte array representation of the {@code TraceOptions}. 149 * @since 0.5 150 * @deprecated use {@link #getByte()}. 151 */ 152 @Deprecated getBytes()153 public byte[] getBytes() { 154 byte[] bytes = new byte[SIZE]; 155 bytes[0] = options; 156 return bytes; 157 } 158 159 /** 160 * Copies the byte representations of the {@code TraceOptions} into the {@code dest} beginning at 161 * the {@code destOffset} offset. 162 * 163 * <p>Equivalent with (but faster because it avoids any new allocations): 164 * 165 * <pre>{@code 166 * System.arraycopy(getBytes(), 0, dest, destOffset, TraceOptions.SIZE); 167 * }</pre> 168 * 169 * @param dest the destination buffer. 170 * @param destOffset the starting offset in the destination buffer. 171 * @throws NullPointerException if {@code dest} is null. 172 * @throws IndexOutOfBoundsException if {@code destOffset+TraceOptions.SIZE} is greater than 173 * {@code dest.length}. 174 * @since 0.5 175 */ copyBytesTo(byte[] dest, int destOffset)176 public void copyBytesTo(byte[] dest, int destOffset) { 177 Utils.checkIndex(destOffset, dest.length); 178 dest[destOffset] = options; 179 } 180 181 /** 182 * Copies the lowercase base16 representations of the {@code TraceId} into the {@code dest} 183 * beginning at the {@code destOffset} offset. 184 * 185 * @param dest the destination buffer. 186 * @param destOffset the starting offset in the destination buffer. 187 * @throws IndexOutOfBoundsException if {@code destOffset + 2} is greater than {@code 188 * dest.length}. 189 * @since 0.18 190 */ copyLowerBase16To(char[] dest, int destOffset)191 public void copyLowerBase16To(char[] dest, int destOffset) { 192 BigendianEncoding.byteToBase16String(options, dest, destOffset); 193 } 194 195 /** 196 * Returns the lowercase base16 encoding of this {@code TraceOptions}. 197 * 198 * @return the lowercase base16 encoding of this {@code TraceOptions}. 199 * @since 0.18 200 */ toLowerBase16()201 public String toLowerBase16() { 202 char[] chars = new char[BASE16_SIZE]; 203 copyLowerBase16To(chars, 0); 204 return new String(chars); 205 } 206 207 /** 208 * Returns a new {@link Builder} with default options. 209 * 210 * @return a new {@code Builder} with default options. 211 * @since 0.5 212 */ builder()213 public static Builder builder() { 214 return new Builder(DEFAULT_OPTIONS); 215 } 216 217 /** 218 * Returns a new {@link Builder} with all given options set. 219 * 220 * @param traceOptions the given options set. 221 * @return a new {@code Builder} with all given options set. 222 * @since 0.5 223 */ builder(TraceOptions traceOptions)224 public static Builder builder(TraceOptions traceOptions) { 225 return new Builder(traceOptions.options); 226 } 227 228 /** 229 * Returns a boolean indicating whether this {@code Span} is part of a sampled trace and data 230 * should be exported to a persistent store. 231 * 232 * @return a boolean indicating whether the trace is sampled. 233 * @since 0.5 234 */ isSampled()235 public boolean isSampled() { 236 return hasOption(IS_SAMPLED); 237 } 238 239 @Override equals(@ullable Object obj)240 public boolean equals(@Nullable Object obj) { 241 if (obj == this) { 242 return true; 243 } 244 245 if (!(obj instanceof TraceOptions)) { 246 return false; 247 } 248 249 TraceOptions that = (TraceOptions) obj; 250 return options == that.options; 251 } 252 253 @Override hashCode()254 public int hashCode() { 255 return Arrays.hashCode(new byte[] {options}); 256 } 257 258 @Override toString()259 public String toString() { 260 return "TraceOptions{sampled=" + isSampled() + "}"; 261 } 262 263 /** 264 * Builder class for {@link TraceOptions}. 265 * 266 * @since 0.5 267 */ 268 public static final class Builder { 269 private byte options; 270 Builder(byte options)271 private Builder(byte options) { 272 this.options = options; 273 } 274 275 /** 276 * Sets the sampling bit in the options to true. 277 * 278 * @deprecated Use {@code Builder.setIsSampled(true)}. 279 * @return this. 280 * @since 0.5 281 */ 282 @Deprecated setIsSampled()283 public Builder setIsSampled() { 284 return setIsSampled(true); 285 } 286 287 /** 288 * Sets the sampling bit in the options. 289 * 290 * @param isSampled the sampling bit. 291 * @return this. 292 * @since 0.7 293 */ setIsSampled(boolean isSampled)294 public Builder setIsSampled(boolean isSampled) { 295 if (isSampled) { 296 options = (byte) (options | IS_SAMPLED); 297 } else { 298 options = (byte) (options & ~IS_SAMPLED); 299 ; 300 } 301 return this; 302 } 303 304 /** 305 * Builds and returns a {@code TraceOptions} with the desired options. 306 * 307 * @return a {@code TraceOptions} with the desired options. 308 * @since 0.5 309 */ build()310 public TraceOptions build() { 311 return fromByte(options); 312 } 313 } 314 315 // Returns the current set of options bitmask. 316 @DefaultVisibilityForTesting getOptions()317 byte getOptions() { 318 return options; 319 } 320 hasOption(int mask)321 private boolean hasOption(int mask) { 322 return (this.options & mask) != 0; 323 } 324 } 325