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

com.undefinedlabs.scope.propagation.internal.TextMapCodec Maven / Gradle / Ivy

package com.undefinedlabs.scope.propagation.internal;

import com.undefinedlabs.scope.SpanContext;
import com.undefinedlabs.scope.TraceId;
import org.apache.commons.lang3.StringUtils;
import com.undefinedlabs.scope.propagation.Codec;
import com.undefinedlabs.scope.utils.UrlUtils;
import io.opentracing.propagation.TextMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TextMapCodec implements Codec {

  // OpenTracing
  private static final String PREFIX_TRACE_STATE = "ot-tracer-";
  protected static final String PREFIX_BAGGAGE = "ot-baggage-";
  protected static final String OT_FIELD_TRACE_ID = PREFIX_TRACE_STATE + "traceid";
  protected static final String OT_FIELD_SPAN_ID = PREFIX_TRACE_STATE + "spanid";
  protected static final String OT_FIELD_PARENT_SPAN_ID = PREFIX_TRACE_STATE + "parentspanid";
  protected static final String OT_FIELD_SAMPLED = PREFIX_TRACE_STATE + "sampled";

  // OpenTelemetry
  protected static final String OTEL_TRACEPARENT = "traceparent";
  protected static final String OTEL_TRACESTATE = "tracestate";
  private static final String OTEL_VERSION = "00";
  private static final String OTEL_TRACEPARENT_DELIMITER = "-";
  private static final String OTEL_FLAGS_SAMPLED = "01";
  private static final String ZEROS_IDS = "00000000000000000000000000000000";
  protected static final String OTEL_TRACESTATE_VENDOR = "scope";

  private final boolean useUrlEncoding;

  private TextMapCodec(final Builder builder) {
    useUrlEncoding = builder.useUrlEncoding;
  }

  @Override
  public void inject(final SpanContext spanContext, final TextMap carrier) {
    // OpenTracing
    carrier.put(
        OT_FIELD_TRACE_ID,
        useUrlEncoding ? UrlUtils.encodeUtf8(spanContext.toTraceId()) : spanContext.toTraceId());
    carrier.put(
        OT_FIELD_SPAN_ID,
        useUrlEncoding
            ? UrlUtils.encodeUtf8(spanContext.toSpanId64bitStr())
            : spanContext.toSpanId64bitStr());
    carrier.put(
        OT_FIELD_PARENT_SPAN_ID,
        useUrlEncoding
            ? UrlUtils.encodeUtf8(spanContext.toParentSpanId64bitStr())
            : spanContext.toParentSpanId64bitStr());
    carrier.put(OT_FIELD_SAMPLED, "true");

    final List otelTracestateList = new ArrayList<>();
    for (final Map.Entry baggageItem : spanContext.baggageItems()) {
      final String key =
          useUrlEncoding ? UrlUtils.encodeUtf8(baggageItem.getKey()) : baggageItem.getKey();
      final String value =
          useUrlEncoding ? UrlUtils.encodeUtf8(baggageItem.getValue()) : baggageItem.getValue();

      carrier.put(PREFIX_BAGGAGE + key, value);
      otelTracestateList.add(OTEL_TRACESTATE_VENDOR + "/" + key.replace(".", "-") + "=" + value);
    }

    // OpenTelemetry
    final String otelSb =
        OTEL_VERSION
            + OTEL_TRACEPARENT_DELIMITER
            + spanContext.toTraceId()
            + OTEL_TRACEPARENT_DELIMITER
            + spanContext.toSpanId()
            + OTEL_TRACEPARENT_DELIMITER
            + OTEL_FLAGS_SAMPLED;
    carrier.put(OTEL_TRACEPARENT, otelSb);
    carrier.put(OTEL_TRACESTATE, StringUtils.join(otelTracestateList, ","));
  }

  @Override
  public SpanContext extract(final TextMap carrier) {
    final Map otelIdsMap = new HashMap<>();
    final Map otracingIdsMap = new HashMap<>();
    final Map baggage = new HashMap<>();

    boolean hasTracestate = false;
    for (final Map.Entry carrierEntry : carrier) {
      if (carrierEntry.getKey().toLowerCase().equals(OTEL_TRACEPARENT)) {
        final String traceparent =
            useUrlEncoding ? UrlUtils.decodeUtf8(carrierEntry.getValue()) : carrierEntry.getValue();
        final String[] traceparentChunks = traceparent.split(OTEL_TRACEPARENT_DELIMITER);
        otelIdsMap.put(OT_FIELD_TRACE_ID, traceparentChunks[1]);
        otelIdsMap.put(OT_FIELD_SPAN_ID, traceparentChunks[2]);
      }

      if (carrierEntry.getKey().toLowerCase().equals(OTEL_TRACESTATE)) {
        hasTracestate = true;
        final String otelTracestate =
            useUrlEncoding ? UrlUtils.decodeUtf8(carrierEntry.getValue()) : carrierEntry.getValue();
        final String[] otelTracestateChunks = otelTracestate.split(",");
        for (final String chunk : otelTracestateChunks) {
          final String[] keyValue = chunk.split("=");
          if (keyValue[0].toLowerCase().startsWith(OTEL_TRACESTATE_VENDOR)) {
            baggage.put(
                keyValue[0]
                    .toLowerCase()
                    .replace(OTEL_TRACESTATE_VENDOR + "/", "")
                    .replace("-", "."),
                keyValue[1]);
          }
        }
      }

      if (carrierEntry.getKey().startsWith(PREFIX_BAGGAGE) && !hasTracestate) {
        baggage.put(
            carrierEntry.getKey().substring(PREFIX_BAGGAGE.length()),
            useUrlEncoding
                ? UrlUtils.decodeUtf8(carrierEntry.getValue())
                : carrierEntry.getValue());
      } else {
        otracingIdsMap.put(
            carrierEntry.getKey(),
            useUrlEncoding
                ? UrlUtils.decodeUtf8(carrierEntry.getValue())
                : carrierEntry.getValue());
      }
    }

    return !otelIdsMap.isEmpty()
        ? createSpanContext(otelIdsMap, baggage)
        : createSpanContext(otracingIdsMap, baggage);
  }

  private SpanContext createSpanContext(
      final Map idsMap, final Map baggage) {
    final String traceIdLowerCaseHex = idsMap.get(OT_FIELD_TRACE_ID);
    final String spanIdLowerCaseHex = idsMap.get(OT_FIELD_SPAN_ID);
    final String parentSpanIdLowerCaseHex = idsMap.get(OT_FIELD_PARENT_SPAN_ID);

    return new SpanContext(
        (traceIdLowerCaseHex == null)
            ? TraceId.getInvalid()
            : TraceId.fromLowerCaseBase16(
                ZEROS_IDS.substring(traceIdLowerCaseHex.length()) + traceIdLowerCaseHex, 0),
        (spanIdLowerCaseHex == null)
            ? TraceId.getInvalid()
            : TraceId.fromLowerCaseBase16(
                ZEROS_IDS.substring(spanIdLowerCaseHex.length()) + spanIdLowerCaseHex, 0),
        (parentSpanIdLowerCaseHex == null)
            ? null
            : TraceId.fromLowerCaseBase16(
                ZEROS_IDS.substring(parentSpanIdLowerCaseHex.length()) + parentSpanIdLowerCaseHex,
                0),
        baggage);
  }

  public static Builder newBuilder() {
    return new Builder();
  }

  public static class Builder {

    private boolean useUrlEncoding;

    public Builder withUseUrlEncoding(final boolean useUrlEncoding) {
      this.useUrlEncoding = useUrlEncoding;
      return this;
    }

    public TextMapCodec build() {
      return new TextMapCodec(this);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy