All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.wavefront.opentracing.propagation.JaegerWavefrontPropagator Maven / Gradle / Ivy

Go to download

Implements OpenTracing API for collecting and sending tracing data to Wavefront from Java applications.

The newest version!
package com.wavefront.opentracing.propagation;

import com.wavefront.opentracing.WavefrontSpanContext;

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.URLDecoder;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import javax.annotation.Nullable;

import io.opentracing.propagation.TextMap;

/**
 * Bridge for extracting/injecting Jaeger headers to/from a WavefrontSpanContext.
 *
 * Essentially allows for extracting Jaeger HTTP headers and creating a WavefrontSpanContext or
 * injecting Jaeger aware HTTP headers from a WavefrontSpanContext.
 *
 * @author [email protected]
 *
 * 

* Example usage: * *

{@code
 * JaegerWavefrontPropagator propogator = new JaegerWavefrontPropagator.Builder()
 *                                         .withBaggagePrefix("uberctx-")
 *                                         .withTraceIdHeader("uber-trace-id").build();
 * tracerBuilder = new WavefrontTracer.Builder(..);
 * tracerBuilder.registerPropagator(Format.Builtin.HTTP_HEADERS, propogator);
 * tracerBuilder.registerPropagator(Format.Builtin.TEXT_MAP, propogator);
 * WavefrontTracer tracer = tracerBuilder.build();
 * ...
 * }
* *

*/ public class JaegerWavefrontPropagator implements Propagator { private static final String BAGGAGE_PREFIX = "baggage-"; private static final String TRACE_ID_KEY = "trace-id"; private static final String PARENT_ID_KEY = "parent-id"; private static final String SAMPLING_DECISION_KEY = "sampling-decision"; private final String traceIdHeader; private final String baggagePrefix; private JaegerWavefrontPropagator(Builder builder) { this.traceIdHeader = builder.traceIdHeader; this.baggagePrefix = builder.baggagePrefix; } @Nullable @Override public WavefrontSpanContext extract(TextMap carrier) { UUID traceId = null; UUID spanId = null; String parentId = null; Boolean samplingDecision = null; Map baggage = new HashMap<>(); for (Map.Entry entry : carrier) { String k = entry.getKey().toLowerCase(); if (k.equalsIgnoreCase(traceIdHeader)) { String[] traceData = contextFromTraceIdHeader(entry.getValue()); if (traceData == null) { continue; } traceId = toUuid(traceData[0]); spanId = toUuid(traceData[1]); // setting parentId as current spanId parentId = spanId.toString(); samplingDecision = traceData[3].equals("1"); } else if (k.startsWith(baggagePrefix.toLowerCase())) { baggage.put(strippedPrefix(entry.getKey()), entry.getValue()); } } if (traceId == null || spanId == null) { return null; } if (parentId.trim().length() > 0 && !"null".equals(parentId)) { baggage.put(PARENT_ID_KEY, parentId); } return new WavefrontSpanContext(traceId, spanId, baggage, samplingDecision); } @Override public void inject(WavefrontSpanContext spanContext, TextMap carrier) { carrier.put(traceIdHeader, contextToTraceIdHeader(spanContext)); for (Map.Entry entry : spanContext.baggageItems()) { carrier.put(baggagePrefix + entry.getKey(), entry.getValue()); } if (spanContext.isSampled()) { carrier.put(SAMPLING_DECISION_KEY, spanContext.getSamplingDecision().toString()); } } /** * Extracts traceId, spanId, parentId and samplingDecision from the 'uber-trace-id' HTTP header * value that's in the format traceId:spanId:parentId:samplingDecision. * * @param value 'uber-trace-id' header value * @return extracted string array with ids */ @Nullable private String[] contextFromTraceIdHeader(String value) { if (value == null || value.equals("")) { return null; } try { // Jaeger HTTP headers may be URL encoded, so we need to decode before splitting value = URLDecoder.decode(value, "UTF-8"); } catch (UnsupportedEncodingException e) { return null; } String[] toks = value.split(":"); if (toks.length != 4) { return null; } if (toks[0] == null || toks[0].length() == 0) { return null; } return toks; } /** * Extracts traceId and spanId from a WavefrontSpanContext and constructs a Jaeger client * compatible header of the form traceId:spanId:parentId:samplingDecision. * * @param context a WavefrontSpanContext * @return formatted header as string */ private String contextToTraceIdHeader(WavefrontSpanContext context) { BigInteger traceId = uuidToBigInteger(context.getTraceId()); BigInteger spanId = uuidToBigInteger(context.getSpanId()); BigInteger parentId; try { parentId = uuidToBigInteger(UUID.fromString(context.getBaggageItem(PARENT_ID_KEY))); } catch (Exception e) { parentId = BigInteger.ZERO; } Boolean samplingDecision = context.getSamplingDecision(); if (samplingDecision == null) { samplingDecision = false; } StringBuilder outCtx = new StringBuilder(); outCtx.append(traceId.toString(16)).append(":"). append(spanId.toString(16)).append(":"). append(parentId.toString(16)).append(":"). append(samplingDecision ? "1" : "0"); return outCtx.toString(); } private String strippedPrefix(String val) { return val.substring(baggagePrefix.length()); } /** * Parses a UUID and converts to BigInteger. * * @param id UUID of the traceId or spanId * @return BigInteger for UUID. */ BigInteger uuidToBigInteger(UUID id) { ByteBuffer bb = ByteBuffer.wrap(new byte[16]); bb.putLong(id.getMostSignificantBits()); bb.putLong(id.getLeastSignificantBits()); return new BigInteger(1, bb.array()); } /** * Parses a full (low + high) traceId, trimming the lower 64 bits. * Inspired by {@code io.jaegertracing.internal.propagation} * * @param hexString a full traceId * @return the long value of the higher 64 bits for a 128 bit traceId or 0 for 64 bit traceIds */ private static long high(String hexString) { if (hexString.length() <= 16) { return 0L; } int highLength = hexString.length() - 16; String highString = hexString.substring(0, highLength); return new BigInteger(highString, 16).longValue(); } /** * Constructs UUID for traceId/spanId represented as hexString consisting of (low + high) 64 bits. * * UUID is generated with long value of high 64 bits(if any) and long value of low 64 bits and is * consistent with the Wavefront approach. * * @param id hexString form of traceId/spanId * @return UUID for traceId/spanId as expected by WavefrontSpanContext */ UUID toUuid(String id) { long idLow = new BigInteger(id, 16).longValue(); long idHigh = high(id); return new UUID(idHigh, idLow); } /** * Gets a new {@link JaegerWavefrontPropagator.Builder} instance. * * @return a {@link JaegerWavefrontPropagator.Builder} */ public static Builder builder() { return new Builder(); } /** * A builder for {@link JaegerWavefrontPropagator} instances. */ public static class Builder { private String traceIdHeader = TRACE_ID_KEY; private String baggagePrefix = BAGGAGE_PREFIX; public Builder withTraceIdHeader(String traceIdHeader) { this.traceIdHeader = traceIdHeader; return this; } public Builder withBaggagePrefix(String baggagePrefix) { this.baggagePrefix = baggagePrefix; return this; } /** * Builds and returns a JaegerWavefrontPropagator instance based on the given configuration. * * @return a {@link JaegerWavefrontPropagator} */ public JaegerWavefrontPropagator build() { return new JaegerWavefrontPropagator(this); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy