/** This is only safe to call after {@link JmsTracing#extractAndClearMessage(Message)} */ static void addB3SingleHeader(Message message, TraceContext context) { try { message.setStringProperty("b3", writeB3SingleFormatWithoutParentId(context)); } catch (JMSException ignored) { // don't crash on wonky exceptions! } }
/** * Like {@link #writeB3SingleFormatWithoutParentId(TraceContext)}, but for carriers with byte * array or byte buffer values. For example, {@link ByteBuffer#wrap(byte[])} can wrap the result. */ public static byte[] writeB3SingleFormatWithoutParentIdAsBytes(TraceContext context) { char[] buffer = getCharBuffer(); int length = writeB3SingleFormat(context, 0L, buffer); return asciiToNewByteArray(buffer, length); }
static boolean notHexFollowsPos(CharSequence b3, int pos, int end) { return (end >= pos + 2) && !isLowerHex(b3.charAt(pos + 1)); }
/** * Writes all B3 defined fields in the trace context, except {@link TraceContext#parentIdAsLong() * parent ID}, to a hyphen delimited string. * * <p>This is appropriate for receivers who understand "b3" single header format, and always do * work in a child span. For example, message consumers always do work in child spans, so message * producers can use this format to save bytes on the wire. On the other hand, RPC clients should * use {@link #writeB3SingleFormat(TraceContext)} instead, as RPC servers often share a span ID * with the client. */ public static String writeB3SingleFormatWithoutParentId(TraceContext context) { char[] buffer = getCharBuffer(); int length = writeB3SingleFormat(context, 0L, buffer); return new String(buffer, 0, length); }
return tryParseSamplingFlags(b3, pos); traceIdHigh = tryParse16HexCharacters(b3, pos, endIndex); pos += 16; // upper 64 bits of the trace ID traceId = tryParse16HexCharacters(b3, pos, endIndex); } else { traceIdHigh = 0L; traceId = tryParse16HexCharacters(b3, pos, endIndex); if (isLowerHex(b3.charAt(pos))) { Platform.get().log("Invalid input: trace ID is too long", null); return null; if (!checkHyphen(b3, pos++)) return null; long spanId = tryParse16HexCharacters(b3, pos, endIndex); if (spanId == 0L) { Platform.get().log("Invalid input: expected a 16 lower hex span ID at offset {0}", pos, null); long parentId = 0L; if (endIndex > pos) { if (isLowerHex(b3.charAt(pos))) { Platform.get().log("Invalid input: span ID is too long", null); return null; if (!checkHyphen(b3, pos++)) return null; boolean afterSampledField = notHexFollowsPos(b3, pos, endIndex); if (endIndex == pos + 1 || afterSampledField) {
@Override public TraceContextOrSamplingFlags extract(C carrier) { if (carrier == null) throw new NullPointerException("carrier == null"); String b3 = getter.get(carrier, b3Key); if (b3 == null) return TraceContextOrSamplingFlags.EMPTY; TraceContextOrSamplingFlags extracted = B3SingleFormat.parseB3SingleFormat(b3); // if null, the trace context is malformed so return empty if (extracted == null) return TraceContextOrSamplingFlags.EMPTY; return extracted; } }
static TraceContextOrSamplingFlags tryParseSamplingFlags(CharSequence b3, int pos) { int flags = parseFlags(b3, pos); if (flags == 0) return null; return TraceContextOrSamplingFlags.create(SamplingFlags.toSamplingFlags(flags)); }
static int parseFlags(CharSequence b3, int pos) { int flags; char sampledChar = b3.charAt(pos); if (sampledChar == 'd') { flags = FLAG_SAMPLED_SET | FLAG_SAMPLED | FLAG_DEBUG; } else if (sampledChar == '1') { flags = FLAG_SAMPLED_SET | FLAG_SAMPLED; } else if (sampledChar == '0') { flags = FLAG_SAMPLED_SET; } else { logInvalidSampled(pos); flags = 0; } return flags; }
/** Returns zero if truncated, malformed, or too big after logging */ static long tryParseParentId(CharSequence b3, int pos, int endIndex) { if (endIndex < pos + 16) { Platform.get().log("Invalid input: truncated", null); return 0L; } long parentId = tryParse16HexCharacters(b3, pos, endIndex); if (parentId == 0L) { Platform.get() .log("Invalid input: expected a 16 lower hex parent ID at offset {0}", pos, null); return 0L; } pos += 16; if (endIndex != pos) { Platform.get().log("Invalid input: parent ID is too long", null); return 0L; } return parentId; }
return tryParseSamplingFlags(b3, pos); traceIdHigh = tryParse16HexCharacters(b3, pos, endIndex); pos += 16; // upper 64 bits of the trace ID traceId = tryParse16HexCharacters(b3, pos, endIndex); } else { traceIdHigh = 0L; traceId = tryParse16HexCharacters(b3, pos, endIndex); if (isLowerHex(b3.charAt(pos))) { Platform.get().log("Invalid input: trace ID is too long", null); return null; if (!checkHyphen(b3, pos++)) return null; long spanId = tryParse16HexCharacters(b3, pos, endIndex); if (spanId == 0L) { Platform.get().log("Invalid input: expected a 16 lower hex span ID at offset {0}", pos, null); long parentId = 0L; if (endIndex > pos) { if (isLowerHex(b3.charAt(pos))) { Platform.get().log("Invalid input: span ID is too long", null); return null; if (!checkHyphen(b3, pos++)) return null; boolean afterSampledField = notHexFollowsPos(b3, pos, endIndex); if (endIndex == pos + 1 || afterSampledField) {
/** * Writes all B3 defined fields in the trace context to a hyphen delimited string. This is * appropriate for receivers who understand "b3" single header format. * * <p>The {@link TraceContext#parentIdAsLong() parent ID} is serialized in case the receiver is * an RPC server. When downstream is known to be a messaging consumer, or a server that never * reuses a client's span ID, prefer {@link #writeB3SingleFormatWithoutParentId(TraceContext)}. */ public static String writeB3SingleFormat(TraceContext context) { char[] buffer = getCharBuffer(); int length = writeB3SingleFormat(context, context.parentIdAsLong(), buffer); return new String(buffer, 0, length); }
@Nullable public static TraceContextOrSamplingFlags parseB3SingleFormat(CharSequence b3) { return parseB3SingleFormat(b3, 0, b3.length()); }
static TraceContextOrSamplingFlags tryParseSamplingFlags(CharSequence b3, int pos) { int flags = parseFlags(b3, pos); if (flags == 0) return null; return TraceContextOrSamplingFlags.create(SamplingFlags.toSamplingFlags(flags)); }
static int parseFlags(CharSequence b3, int pos) { int flags; char sampledChar = b3.charAt(pos); if (sampledChar == 'd') { flags = FLAG_SAMPLED_SET | FLAG_SAMPLED | FLAG_DEBUG; } else if (sampledChar == '1') { flags = FLAG_SAMPLED_SET | FLAG_SAMPLED; } else if (sampledChar == '0') { flags = FLAG_SAMPLED_SET; } else { logInvalidSampled(pos); flags = 0; } return flags; }
/** Returns zero if truncated, malformed, or too big after logging */ static long tryParseParentId(CharSequence b3, int pos, int endIndex) { if (endIndex < pos + 16) { Platform.get().log("Invalid input: truncated", null); return 0L; } long parentId = tryParse16HexCharacters(b3, pos, endIndex); if (parentId == 0L) { Platform.get() .log("Invalid input: expected a 16 lower hex parent ID at offset {0}", pos, null); return 0L; } pos += 16; if (endIndex != pos) { Platform.get().log("Invalid input: parent ID is too long", null); return 0L; } return parentId; }
/** * Like {@link #writeB3SingleFormatAsBytes(TraceContext)}, but for carriers with byte array or * byte buffer values. For example, {@link ByteBuffer#wrap(byte[])} can wrap the result. */ public static byte[] writeB3SingleFormatAsBytes(TraceContext context) { char[] buffer = getCharBuffer(); int length = writeB3SingleFormat(context, context.parentIdAsLong(), buffer); return asciiToNewByteArray(buffer, length); }
/** * Writes all B3 defined fields in the trace context, except {@link TraceContext#parentIdAsLong() * parent ID}, to a hyphen delimited string. * * <p>This is appropriate for receivers who understand "b3" single header format, and always do * work in a child span. For example, message consumers always do work in child spans, so message * producers can use this format to save bytes on the wire. On the other hand, RPC clients should * use {@link #writeB3SingleFormat(TraceContext)} instead, as RPC servers often share a span ID * with the client. */ public static String writeB3SingleFormatWithoutParentId(TraceContext context) { char[] buffer = getCharBuffer(); int length = writeB3SingleFormat(context, 0L, buffer); return new String(buffer, 0, length); }
@Override public void inject(TraceContext traceContext, MessageProperties carrier) { carrier.setHeader("b3", writeB3SingleFormatWithoutParentId(traceContext)); }
@Override public TraceContextOrSamplingFlags extract(C carrier) { if (carrier == null) throw new NullPointerException("carrier == null"); String b3 = getter.get(carrier, b3Key); if (b3 == null) return TraceContextOrSamplingFlags.EMPTY; TraceContextOrSamplingFlags extracted = B3SingleFormat.parseB3SingleFormat(b3); // if null, the trace context is malformed so return empty if (extracted == null) return TraceContextOrSamplingFlags.EMPTY; return extracted; } }
static boolean notHexFollowsPos(CharSequence b3, int pos, int end) { return (end >= pos + 2) && !isLowerHex(b3.charAt(pos + 1)); }