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

zipkin2.internal.Proto3ZipkinFields Maven / Gradle / Ivy

There is a newer version: 3.4.2
Show newest version
/*
 * Copyright The OpenZipkin Authors
 * SPDX-License-Identifier: Apache-2.0
 */
package zipkin2.internal;

import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import zipkin2.Annotation;
import zipkin2.Endpoint;
import zipkin2.Span;
import zipkin2.internal.Proto3Fields.BooleanField;
import zipkin2.internal.Proto3Fields.Utf8Field;

import static java.util.logging.Level.FINE;
import static zipkin2.internal.Proto3Fields.BytesField;
import static zipkin2.internal.Proto3Fields.Field.fieldNumber;
import static zipkin2.internal.Proto3Fields.Field.skipValue;
import static zipkin2.internal.Proto3Fields.Field.wireType;
import static zipkin2.internal.Proto3Fields.Fixed64Field;
import static zipkin2.internal.Proto3Fields.HexField;
import static zipkin2.internal.Proto3Fields.LengthDelimitedField;
import static zipkin2.internal.Proto3Fields.VarintField;
import static zipkin2.internal.Proto3Fields.WIRETYPE_FIXED64;
import static zipkin2.internal.Proto3Fields.WIRETYPE_LENGTH_DELIMITED;
import static zipkin2.internal.Proto3Fields.WIRETYPE_VARINT;

/** Keys are used in this class because while verbose, it allows us to use switch statements */
//@Immutable
final class Proto3ZipkinFields {
  static final Logger LOG = Logger.getLogger(Proto3ZipkinFields.class.getName());
  /** This is the only field in the ListOfSpans type */
  static final SpanField SPAN = new SpanField();

  static class EndpointField extends LengthDelimitedField {
    static final int SERVICE_NAME_KEY = (1 << 3) | WIRETYPE_LENGTH_DELIMITED;
    static final int IPV4_KEY = (2 << 3) | WIRETYPE_LENGTH_DELIMITED;
    static final int IPV6_KEY = (3 << 3) | WIRETYPE_LENGTH_DELIMITED;
    static final int PORT_KEY = (4 << 3) | WIRETYPE_VARINT;

    static final Utf8Field SERVICE_NAME = new Utf8Field(SERVICE_NAME_KEY);
    static final BytesField IPV4 = new BytesField(IPV4_KEY);
    static final BytesField IPV6 = new BytesField(IPV6_KEY);
    static final VarintField PORT = new VarintField(PORT_KEY);

    EndpointField(int key) {
      super(key);
    }

    @Override int sizeOfValue(Endpoint value) {
      int result = 0;
      result += SERVICE_NAME.sizeInBytes(value.serviceName());
      if (value.ipv4Bytes() != null) result += 6; // tag + size of 4 + 4 bytes
      if (value.ipv6Bytes() != null) result += 18; // tag + size of 16 + 16 bytes
      result += PORT.sizeInBytes(value.portAsInt());
      return result;
    }

    @Override void writeValue(WriteBuffer b, Endpoint value) {
      SERVICE_NAME.write(b, value.serviceName());
      IPV4.write(b, value.ipv4Bytes());
      IPV6.write(b, value.ipv6Bytes());
      PORT.write(b, value.portAsInt());
    }

    @Override Endpoint readValue(ReadBuffer buffer, int length) {
      int endPos = buffer.pos() + length;

      // now, we are in the endpoint fields
      Endpoint.Builder builder = Endpoint.newBuilder();
      while (buffer.pos() < endPos) {
        int nextKey = buffer.readVarint32();
        switch (nextKey) {
          case SERVICE_NAME_KEY:
            builder.serviceName(SERVICE_NAME.readLengthPrefixAndValue(buffer));
            break;
          case IPV4_KEY:
            builder.parseIp(IPV4.readLengthPrefixAndValue(buffer));
            break;
          case IPV6_KEY:
            builder.parseIp(IPV6.readLengthPrefixAndValue(buffer));
            break;
          case PORT_KEY:
            builder.port(buffer.readVarint32());
            break;
          default:
            logAndSkip(buffer, nextKey);
        }
      }
      return builder.build();
    }
  }

  /** Contributes to a builder as opposed to reading values directly. Avoids allocation. */
  static abstract class SpanBuilderField extends LengthDelimitedField {

    SpanBuilderField(int key) {
      super(key);
    }

    @Override final T readValue(ReadBuffer b, int length) {
      throw new UnsupportedOperationException();
    }

    abstract boolean readLengthPrefixAndValue(ReadBuffer b, Span.Builder builder);
  }

  static class AnnotationField extends SpanBuilderField {
    static final int TIMESTAMP_KEY = (1 << 3) | WIRETYPE_FIXED64;
    static final int VALUE_KEY = (2 << 3) | WIRETYPE_LENGTH_DELIMITED;

    static final Fixed64Field TIMESTAMP = new Fixed64Field(TIMESTAMP_KEY);
    static final Utf8Field VALUE = new Utf8Field(VALUE_KEY);

    AnnotationField(int key) {
      super(key);
    }

    @Override int sizeOfValue(Annotation value) {
      return TIMESTAMP.sizeInBytes(value.timestamp()) + VALUE.sizeInBytes(value.value());
    }

    @Override void writeValue(WriteBuffer b, Annotation value) {
      TIMESTAMP.write(b, value.timestamp());
      VALUE.write(b, value.value());
    }

    @Override boolean readLengthPrefixAndValue(ReadBuffer b, Span.Builder builder) {
      int length = b.readVarint32();
      if (length == 0) return false;
      int endPos = b.pos() + length;

      // now, we are in the annotation fields
      long timestamp = 0L;
      String value = null;
      while (b.pos() < endPos) {
        int nextKey = b.readVarint32();
        switch (nextKey) {
          case TIMESTAMP_KEY:
            timestamp = TIMESTAMP.readValue(b);
            break;
          case VALUE_KEY:
            value = VALUE.readLengthPrefixAndValue(b);
            break;
          default:
            logAndSkip(b, nextKey);
        }
      }
      if (timestamp == 0L || value == null) return false;
      builder.addAnnotation(timestamp, value);
      return true;
    }
  }

  static final class TagField extends SpanBuilderField> {
    // map in proto is a special field with key, value
    static final int KEY_KEY = (1 << 3) | WIRETYPE_LENGTH_DELIMITED;
    static final int VALUE_KEY = (2 << 3) | WIRETYPE_LENGTH_DELIMITED;

    static final Utf8Field KEY = new Utf8Field(KEY_KEY);
    static final Utf8Field VALUE = new Utf8Field(VALUE_KEY);

    TagField(int key) {
      super(key);
    }

    @Override int sizeOfValue(Map.Entry value) {
      return KEY.sizeInBytes(value.getKey()) + VALUE.sizeInBytes(value.getValue());
    }

    @Override void writeValue(WriteBuffer b, Map.Entry value) {
      KEY.write(b, value.getKey());
      VALUE.write(b, value.getValue());
    }

    @Override boolean readLengthPrefixAndValue(ReadBuffer b, Span.Builder builder) {
      int length = b.readVarint32();
      if (length == 0) return false;
      int endPos = b.pos() + length;

      // now, we are in the tag fields
      String key = null, value = ""; // empty tags allowed
      while (b.pos() < endPos) {
        int nextKey = b.readVarint32();
        switch (nextKey) {
          case KEY_KEY:
            key = KEY.readLengthPrefixAndValue(b);
            break;
          case VALUE_KEY:
            String read = VALUE.readLengthPrefixAndValue(b);
            if (read != null) value = read; // empty tags allowed
            break;
          default:
            logAndSkip(b, nextKey);
        }
      }
      if (key == null) return false;
      builder.putTag(key, value);
      return true;
    }
  }

  static class SpanField extends LengthDelimitedField {
    static final int TRACE_ID_KEY = (1 << 3) | WIRETYPE_LENGTH_DELIMITED;
    static final int PARENT_ID_KEY = (2 << 3) | WIRETYPE_LENGTH_DELIMITED;
    static final int ID_KEY = (3 << 3) | WIRETYPE_LENGTH_DELIMITED;
    static final int KIND_KEY = (4 << 3) | WIRETYPE_VARINT;
    static final int NAME_KEY = (5 << 3) | WIRETYPE_LENGTH_DELIMITED;
    static final int TIMESTAMP_KEY = (6 << 3) | WIRETYPE_FIXED64;
    static final int DURATION_KEY = (7 << 3) | WIRETYPE_VARINT;
    static final int LOCAL_ENDPOINT_KEY = (8 << 3) | WIRETYPE_LENGTH_DELIMITED;
    static final int REMOTE_ENDPOINT_KEY = (9 << 3) | WIRETYPE_LENGTH_DELIMITED;
    static final int ANNOTATION_KEY = (10 << 3) | WIRETYPE_LENGTH_DELIMITED;
    static final int TAG_KEY = (11 << 3) | WIRETYPE_LENGTH_DELIMITED;
    static final int DEBUG_KEY = (12 << 3) | WIRETYPE_VARINT;
    static final int SHARED_KEY = (13 << 3) | WIRETYPE_VARINT;

    static final HexField TRACE_ID = new HexField(TRACE_ID_KEY);
    static final HexField PARENT_ID = new HexField(PARENT_ID_KEY);
    static final HexField ID = new HexField(ID_KEY);
    static final VarintField KIND = new VarintField(KIND_KEY);
    static final Utf8Field NAME = new Utf8Field(NAME_KEY);
    static final Fixed64Field TIMESTAMP = new Fixed64Field(TIMESTAMP_KEY);
    static final VarintField DURATION = new VarintField(DURATION_KEY);
    static final EndpointField LOCAL_ENDPOINT = new EndpointField(LOCAL_ENDPOINT_KEY);
    static final EndpointField REMOTE_ENDPOINT = new EndpointField(REMOTE_ENDPOINT_KEY);
    static final AnnotationField ANNOTATION = new AnnotationField(ANNOTATION_KEY);
    static final TagField TAG = new TagField(TAG_KEY);
    static final BooleanField DEBUG = new BooleanField(DEBUG_KEY);
    static final BooleanField SHARED = new BooleanField(SHARED_KEY);

    SpanField() {
      super((1 << 3) | WIRETYPE_LENGTH_DELIMITED);
    }

    @Override int sizeOfValue(Span span) {
      int sizeOfSpan = TRACE_ID.sizeInBytes(span.traceId());
      sizeOfSpan += PARENT_ID.sizeInBytes(span.parentId());
      sizeOfSpan += ID.sizeInBytes(span.id());
      sizeOfSpan += KIND.sizeInBytes(span.kind() != null ? 1 : 0);
      sizeOfSpan += NAME.sizeInBytes(span.name());
      sizeOfSpan += TIMESTAMP.sizeInBytes(span.timestampAsLong());
      sizeOfSpan += DURATION.sizeInBytes(span.durationAsLong());
      sizeOfSpan += LOCAL_ENDPOINT.sizeInBytes(span.localEndpoint());
      sizeOfSpan += REMOTE_ENDPOINT.sizeInBytes(span.remoteEndpoint());

      List annotations = span.annotations();
      for (Annotation annotation : annotations) {
        sizeOfSpan += ANNOTATION.sizeInBytes(annotation);
      }

      Map tags = span.tags();
      int tagCount = tags.size();
      if (tagCount > 0) { // avoid allocating an iterator when empty
        for (Map.Entry entry : tags.entrySet()) {
          sizeOfSpan += TAG.sizeInBytes(entry);
        }
      }

      sizeOfSpan += DEBUG.sizeInBytes(Boolean.TRUE.equals(span.debug()));
      sizeOfSpan += SHARED.sizeInBytes(Boolean.TRUE.equals(span.shared()));
      return sizeOfSpan;
    }

    @Override void writeValue(WriteBuffer b, Span value) {
      TRACE_ID.write(b, value.traceId());
      PARENT_ID.write(b, value.parentId());
      ID.write(b, value.id());
      KIND.write(b, toByte(value.kind()));
      NAME.write(b, value.name());
      TIMESTAMP.write(b, value.timestampAsLong());
      DURATION.write(b, value.durationAsLong());
      LOCAL_ENDPOINT.write(b, value.localEndpoint());
      REMOTE_ENDPOINT.write(b, value.remoteEndpoint());

      List annotations = value.annotations();
      for (Annotation annotation : annotations) {
        ANNOTATION.write(b, annotation);
      }

      Map tags = value.tags();
      if (!tags.isEmpty()) { // avoid allocating an iterator when empty
        for (Map.Entry entry : tags.entrySet()) {
          TAG.write(b, entry);
        }
      }

      SpanField.DEBUG.write(b, Boolean.TRUE.equals(value.debug()));
      SpanField.SHARED.write(b, Boolean.TRUE.equals(value.shared()));
    }

    // in java, there's no zero index for unknown
    int toByte(Span.Kind kind) {
      return kind != null ? kind.ordinal() + 1 : 0;
    }

    public Span read(ReadBuffer buffer) {
      buffer.readVarint32(); // toss the key
      return readLengthPrefixAndValue(buffer);
    }

    @Override Span readValue(ReadBuffer buffer, int length) {
      buffer.require(length); // more convenient to check up-front vs partially read
      int endPos = buffer.pos() + length;

      // now, we are in the span fields
      Span.Builder builder = Span.newBuilder();
      while (buffer.pos() < endPos) {
        int nextKey = buffer.readVarint32();
        switch (nextKey) {
          case TRACE_ID_KEY:
            builder.traceId(TRACE_ID.readLengthPrefixAndValue(buffer));
            break;
          case PARENT_ID_KEY:
            builder.parentId(PARENT_ID.readLengthPrefixAndValue(buffer));
            break;
          case ID_KEY:
            builder.id(ID.readLengthPrefixAndValue(buffer));
            break;
          case KIND_KEY:
            int kind = buffer.readVarint32();
            if (kind == 0) break;
            if (kind > Span.Kind.values().length) break;
            builder.kind(Span.Kind.values()[kind - 1]);
            break;
          case NAME_KEY:
            builder.name(NAME.readLengthPrefixAndValue(buffer));
            break;
          case TIMESTAMP_KEY:
            builder.timestamp(TIMESTAMP.readValue(buffer));
            break;
          case DURATION_KEY:
            builder.duration(buffer.readVarint64());
            break;
          case LOCAL_ENDPOINT_KEY:
            builder.localEndpoint(LOCAL_ENDPOINT.readLengthPrefixAndValue(buffer));
            break;
          case REMOTE_ENDPOINT_KEY:
            builder.remoteEndpoint(REMOTE_ENDPOINT.readLengthPrefixAndValue(buffer));
            break;
          case ANNOTATION_KEY:
            ANNOTATION.readLengthPrefixAndValue(buffer, builder);
            break;
          case TAG_KEY:
            TAG.readLengthPrefixAndValue(buffer, builder);
            break;
          case DEBUG_KEY:
            if (DEBUG.read(buffer)) builder.debug(true);
            break;
          case SHARED_KEY:
            if (SHARED.read(buffer)) builder.shared(true);
            break;
          default:
            logAndSkip(buffer, nextKey);
        }
      }
      return builder.build();
    }
  }

  static void logAndSkip(ReadBuffer buffer, int nextKey) {
    int nextWireType = wireType(nextKey, buffer.pos());
    if (LOG.isLoggable(FINE)) {
      int nextFieldNumber = fieldNumber(nextKey, buffer.pos());
      LOG.fine(String.format("Skipping field: byte=%s, fieldNumber=%s, wireType=%s",
        buffer.pos(), nextFieldNumber, nextWireType));
    }
    skipValue(buffer, nextWireType);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy