brave.propagation.aws.AWSPropagation Maven / Gradle / Ivy
package brave.propagation.aws;
import brave.Tracing;
import brave.internal.Nullable;
import brave.propagation.ExtraFieldPropagation;
import brave.propagation.Propagation;
import brave.propagation.SamplingFlags;
import brave.propagation.TraceContext;
import brave.propagation.TraceContextOrSamplingFlags;
import brave.propagation.TraceIdContext;
import java.util.Collections;
import java.util.List;
import static brave.internal.HexCodec.writeHexByte;
import static brave.internal.HexCodec.writeHexLong;
/**
* Utility for working with Amazon Web Services Trace IDs, for example reading from headers or
* environment variables. {@code x-amzn-trace-id} is primarily for Amazon's X-Ray service, but it is
* also integrated with AWS ALB, API Gateway and Lambda.
*
* For example, if you are in a lambda environment, you can read the incoming context like this:
*
{@code
* span = tracer.nextSpan(AWSPropagation.extractLambda());
* }
*
* Details
{@code x-amzn-trace-id} (and the lambda equivalent {@code _X_AMZN_TRACE_ID})
* follows RFC 6265 style syntax (https://tools.ietf.org/html/rfc6265#section-2.2): fields are split
* on semicolon and optional whitespace.
*
* Description of the {@code Root} (or {@code Self}) field from AWS CLI help:
*
*
A trace_id consists of three numbers separated by hyphens. For example, {@code
* 1-58406520-a006649127e371903a2de979}. This includes:
*
*
* - The version number, i.e. 1
* - The time of the original request, in Unix epoch time, in 8 hexadecimal digits. For example,
* 10:00AM December 2nd, 2016 PST in epoch timeis 1480615200 seconds, or 58406520 in
* hexadecimal.
* - A 96-bit identifier for the trace, globally unique, in 24 hexadecimal digits.
*
*
*/
public final class AWSPropagationEx 74 characters: {@code Root=1-67891233-abcdef012345678912345678;Parent=463ac35c9f6413ad;Sampled=1} * *
{@inheritDoc}
*/
@Override public void inject(TraceContext traceContext, C carrier) {
CharSequence extra = null;
for (int i = 0, length = traceContext.extra().size(); i < length; i++) {
Object next = traceContext.extra().get(i);
if (next instanceof Extra) {
extra = ((Extra) next).fields;
break;
}
}
int extraLength = extra == null ? 0 : extra.length();
//Root=1-67891233-abcdef012345678912345678;Parent=463ac35c9f6413ad;Sampled=1
char[] result = new char[74 + extraLength];
System.arraycopy(ROOT, 0, result, 0, 5);
writeRoot(traceContext, result, 5);
System.arraycopy(PARENT, 0, result, 40, 8);
writeHexLong(result, 48, traceContext.spanId());
System.arraycopy(SAMPLED, 0, result, 64, 9);
Boolean sampled = traceContext.sampled();
// Sampled status is same as B3, but ? means downstream decides (like omitting X-B3-Sampled)
// https://github.com/aws/aws-xray-sdk-go/blob/391885218b556c43ed05a1e736a766d70fc416f1/header/header.go#L50
result[73] = sampled == null ? '?' : sampled ? '1' : '0';
for (int i = 0; i < extraLength; i++) {
result[i + 74] = extra.charAt(i);
}
setter.put(carrier, propagation.traceIdKey, new String(result));
}
}
/** Returns the current {@link #traceId(TraceContext)} or null if not available */
@Nullable public static String currentTraceId() {
Tracing tracing = Tracing.current();
if (tracing == null) return null;
TraceContext context = tracing.currentTraceContext().get();
if (context == null) return null;
return traceId(context);
}
/**
* Used for log correlation or {@link brave.Span#tag(String, String) tag values}
*
* @return a formatted Root field like "1-58406520-a006649127e371903a2de979" or null if the
* context was not created from an instance of {@link AWSPropagation}.
*/
@Nullable public static String traceId(TraceContext context) {
for (int i = 0, length = context.extra().size(); i < length; i++) {
Object next = context.extra().get(i);
if (next instanceof Extra) {
char[] result = new char[ROOT_LENGTH];
writeRoot(context, result, 0);
return new String(result);
}
}
// See if we have the field as a pass-through
String maybeHeader = ExtraFieldPropagation.get(context, TRACE_ID_NAME);
if (maybeHeader == null) return null;
int i = maybeHeader.indexOf("Root=");
if (i == -1) return null;
i += 5; // Root=
if (maybeHeader.length() < i + ROOT_LENGTH) return null;
return maybeHeader.substring(i, i + ROOT_LENGTH);
}
/** Writes 35 characters representing the input trace ID to the buffer at the given offset */
static void writeRoot(TraceContext context, char[] result, int offset) {
result[offset] = '1'; // version
result[offset + 1] = '-'; // delimiter
long high = context.traceIdHigh();
writeHexByte(result, offset + 2, (byte) ((high >>> 56L) & 0xff));
writeHexByte(result, offset + 4, (byte) ((high >>> 48L) & 0xff));
writeHexByte(result, offset + 6, (byte) ((high >>> 40L) & 0xff));
writeHexByte(result, offset + 8, (byte) ((high >>> 32L) & 0xff));
result[offset + 10] = '-';
writeHexByte(result, offset + 11, (byte) ((high >>> 24L) & 0xff));
writeHexByte(result, offset + 13, (byte) ((high >>> 16L) & 0xff));
writeHexByte(result, offset + 15, (byte) ((high >>> 8L) & 0xff));
writeHexByte(result, offset + 17, (byte) (high & 0xff));
writeHexLong(result, offset + 19, context.traceId());
}
@Override public This is used for extracting from the AWS lambda environment variable {@code
* _X_AMZN_TRACE_ID}.
*
* @see #extract(String)
*/
public static TraceContextOrSamplingFlags extractLambda() {
return STRING_EXTRACTOR.extract(System.getenv("_X_AMZN_TRACE_ID"));
}
/**
* Like {@link TraceContext.Extractor#extract(Object)} except reading from a single field.
*/
public static TraceContextOrSamplingFlags extract(String amznTraceId) {
if (amznTraceId == null) return EMPTY;
return STRING_EXTRACTOR.extract(amznTraceId);
}
static final class AWSExtractor