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

zipkin.internal.DetectingSpanDecoder Maven / Gradle / Ivy

/*
 * Copyright 2015-2018 The OpenZipkin Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package zipkin.internal;

import java.util.List;
import zipkin.Span;
import zipkin.SpanDecoder;

/**
 * Detecting decoder used in transports which don't include means to identify the type of the data.
 *
 * 

For example, we can identify the encoding and also the format in http via the request path and * content-type. However, in Kafka it could be that folks send mixed Zipkin data without identifying * its format. For example, Kafka historically has no content-type and users don't always segregate * different queues by instrumentation format. */ // In TBinaryProtocol encoding, the first byte is the TType, in a range 0-16 // .. If the first byte isn't in that range, it isn't a thrift. // // When byte(0) == '[' (91), assume it is a list of json-encoded spans // // When byte(0) == 10, assume it is a proto3-encoded span or trace ID field // // When byte(0) <= 16, assume it is a TBinaryProtocol-encoded thrift // .. When serializing a Span (Struct), the first byte will be the type of a field // .. When serializing a List[ThriftSpan], the first byte is the member type, TType.STRUCT(12) // .. As ThriftSpan has no STRUCT fields: so, if the first byte is TType.STRUCT(12), it is a list. public final class DetectingSpanDecoder implements SpanDecoder { /** * Zipkin v2 json will have "localEndpoint" or "remoteEndpoint" fields, and others won't. * *

Note: Technically, it is also possible that one can thwart this by creating an binary * annotation of type string with a name or value literally ending in Endpoint. This would be * strange, especially as the convention to identify a local endpoint is the key "lc". To prevent * a secondary check, this scenario is also ignored. */ static final byte[] ENDPOINT_FIELD_SUFFIX = "Endpoint\"".getBytes(Util.UTF_8); /** * Technically, it is possible to have a v2 span with no endpoints. This should catch the case * where someone reported a tag without reporting the "localEndpoint". * *

Note: we don't check for annotations as that exists in both v1 and v2 formats. */ static final byte[] TAGS_FIELD = {'"', 't', 'a', 'g', 's', '"'}; static final SpanDecoder JSON2_DECODER = new V2JsonSpanDecoder(); static final SpanDecoder PROTO3_DECODER = new V2Proto3SpanDecoder(); @Override public Span readSpan(byte[] span) { SpanDecoder decoder = detectFormat(span); if (span[0] == 12 /* List[ThriftSpan] */ || span[0] == '[') { throw new IllegalArgumentException("Expected json or thrift object, not list encoding"); } return decoder.readSpan(span); } @Override public List readSpans(byte[] span) { SpanDecoder decoder = detectFormat(span); if (span[0] != 12 /* List[ThriftSpan] */ && !protobuf3(span) && span[0] != '[') { throw new IllegalArgumentException("Expected json, proto3 or thrift list encoding"); } return decoder.readSpans(span); } /** @throws IllegalArgumentException if the input isn't a json or thrift list or object. */ public static SpanDecoder detectFormat(byte[] bytes) { if (bytes[0] <= 16) { // binary format if (protobuf3(bytes)) return PROTO3_DECODER; return THRIFT_DECODER; /* the first byte is the TType, in a range 0-16 */ } else if (bytes[0] != '[' && bytes[0] != '{') { throw new IllegalArgumentException("Could not detect the span format"); } if (contains(bytes, ENDPOINT_FIELD_SUFFIX)) return JSON2_DECODER; if (contains(bytes, TAGS_FIELD)) return JSON2_DECODER; return SpanDecoder.JSON_DECODER; } static boolean contains(byte[] bytes, byte[] subsequence) { bytes: for (int i = 0; i < bytes.length - subsequence.length + 1; i++) { for (int j = 0; j < subsequence.length; j++) { if (bytes[i + j] != subsequence[j]) { continue bytes; } } return true; } return false; } /* span key or trace ID key */ static boolean protobuf3(byte[] bytes) { return bytes[0] == 10 && bytes[1] != 0; // varint follows and won't be zero } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy