diff --git a/benchmarks/src/main/java/zipkin/benchmarks/CodecBenchmarks.java b/benchmarks/src/main/java/zipkin/benchmarks/CodecBenchmarks.java index 50d8a1e6fdb..8bf26515110 100644 --- a/benchmarks/src/main/java/zipkin/benchmarks/CodecBenchmarks.java +++ b/benchmarks/src/main/java/zipkin/benchmarks/CodecBenchmarks.java @@ -42,9 +42,9 @@ import zipkin.Codec; import zipkin.Endpoint; import zipkin.internal.v2.Span; -import zipkin.internal.v2.codec.MessageEncoder; -import zipkin.internal.v2.codec.Decoder; -import zipkin.internal.v2.codec.Encoder; +import zipkin.internal.v2.codec.BytesMessageEncoder; +import zipkin.internal.v2.codec.BytesDecoder; +import zipkin.internal.v2.codec.BytesEncoder; /** * This compares the speed of the bundled java codec with the approach used in the scala @@ -158,19 +158,19 @@ public byte[] writeClientSpan_thrift_libthrift() throws TException { return serialize(clientSpanLibThrift); } - static final Span span2 = Decoder.JSON.decodeList(read("/span2.json")).get(0); - static final byte[] tenClientSpan2sJson = MessageEncoder.JSON_BYTES.encode( - Collections.nCopies(10, span2).stream().map(Encoder.JSON::encode).collect(Collectors.toList()) + static final Span span2 = BytesDecoder.JSON.decodeList(read("/span2.json")).get(0); + static final byte[] tenClientSpan2sJson = BytesMessageEncoder.JSON_TO_BYTES.encode( + Collections.nCopies(10, span2).stream().map(BytesEncoder.JSON::encode).collect(Collectors.toList()) ); @Benchmark public List readTenClientSpans_json_span2() { - return Decoder.JSON.decodeList(tenClientSpan2sJson); + return BytesDecoder.JSON.decodeList(tenClientSpan2sJson); } @Benchmark public byte[] writeClientSpan_json_span2() { - return Encoder.JSON.encode(span2); + return BytesEncoder.JSON.encode(span2); } static final byte[] rpcSpanJson = read("/span-rpc.json"); @@ -246,7 +246,7 @@ public byte[] writeRpcV6Span_thrift_libthrift() throws TException { // Convenience main entry-point public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() - .include("CodecBenchmarks.readTenClientSpans_json_span2") + .include(".*" + CodecBenchmarks.class.getSimpleName() + ".*ClientSpan.*") .build(); new Runner(opt).run(); diff --git a/benchmarks/src/main/java/zipkin/benchmarks/Span2ConverterBenchmarks.java b/benchmarks/src/main/java/zipkin/benchmarks/Span2ConverterBenchmarks.java index 08894d3ea77..3601e3e1222 100644 --- a/benchmarks/src/main/java/zipkin/benchmarks/Span2ConverterBenchmarks.java +++ b/benchmarks/src/main/java/zipkin/benchmarks/Span2ConverterBenchmarks.java @@ -38,6 +38,8 @@ import zipkin.internal.V2SpanConverter; import zipkin.internal.Util; +import static zipkin.internal.V2SpanConverter.convert; + @Measurement(iterations = 5, time = 1) @Warmup(iterations = 10, time = 1) @Fork(3) @@ -88,15 +90,15 @@ public class Span2ConverterBenchmarks { .addBinaryAnnotation(BinaryAnnotation.address(Constants.CLIENT_ADDR, frontend)) .build(); - Span server2 = Span.builder() + Span server2 = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .parentId("6b221d5bc9e6496c") .id("5b4185666d50f68b") .name("get") .kind(Span.Kind.SERVER) .shared(true) - .localEndpoint(backend) - .remoteEndpoint(frontend) + .localEndpoint(convert(backend)) + .remoteEndpoint(convert(frontend)) .timestamp(1472470996250000L) .duration(100000L) .putTag(TraceKeys.HTTP_PATH, "/backend") diff --git a/benchmarks/src/main/java/zipkin/benchmarks/SpanBenchmarks.java b/benchmarks/src/main/java/zipkin/benchmarks/SpanBenchmarks.java index 8391af22e2f..6d1e5da24e9 100644 --- a/benchmarks/src/main/java/zipkin/benchmarks/SpanBenchmarks.java +++ b/benchmarks/src/main/java/zipkin/benchmarks/SpanBenchmarks.java @@ -33,8 +33,8 @@ import zipkin.Constants; import zipkin.Endpoint; import zipkin.TraceKeys; -import zipkin.internal.v2.Span; import zipkin.internal.Util; +import zipkin.internal.v2.Span; @Measurement(iterations = 5, time = 1) @Warmup(iterations = 10, time = 1) @@ -79,7 +79,15 @@ public zipkin.Span buildLocalSpan() { .ipv4(192 << 24 | 168 << 16 | 99 << 8 | 101) .port(9000) .build(); - + static final zipkin.internal.v2.Endpoint frontend2 = zipkin.internal.v2.Endpoint.newBuilder() + .serviceName("frontend") + .ip("127.0.0.1") + .build(); + static final zipkin.internal.v2.Endpoint backend2 = zipkin.internal.v2.Endpoint.newBuilder() + .serviceName("backend") + .ip("192.168.99.101") + .port(9000) + .build(); @Benchmark public zipkin.Span buildClientOnlySpan() { return buildClientOnlySpan(zipkin.Span.builder()); @@ -110,7 +118,7 @@ public zipkin.Span buildClientOnlySpan_clear() { @Benchmark public Span buildClientOnlySpan2() { - return buildClientOnlySpan2(Span.builder()); + return buildClientOnlySpan2(Span.newBuilder()); } static Span buildClientOnlySpan2(Span.Builder builder) { @@ -120,8 +128,8 @@ static Span buildClientOnlySpan2(Span.Builder builder) { .id(spanIdHex) .name("get") .kind(Span.Kind.CLIENT) - .localEndpoint(frontend) - .remoteEndpoint(backend) + .localEndpoint(frontend2) + .remoteEndpoint(backend2) .timestamp(1472470996199000L) .duration(207000L) .addAnnotation(1472470996238000L, Constants.WIRE_SEND) diff --git a/benchmarks/src/main/resources/span-client.json b/benchmarks/src/main/resources/span-client.json index 466eb2a29ae..da8b4b65fb4 100644 --- a/benchmarks/src/main/resources/span-client.json +++ b/benchmarks/src/main/resources/span-client.json @@ -1,5 +1,5 @@ { - "traceId": "86154a4ba6e91385", + "traceId": "4d1e00c0db9010db86154a4ba6e91385", "name": "get", "id": "4d1e00c0db9010db", "parentId": "86154a4ba6e91385", diff --git a/benchmarks/src/main/resources/span2.json b/benchmarks/src/main/resources/span2.json index 7dbc990d325..ab4cdc217b6 100644 --- a/benchmarks/src/main/resources/span2.json +++ b/benchmarks/src/main/resources/span2.json @@ -1,5 +1,5 @@ [{ - "traceId": "86154a4ba6e91385", + "traceId": "4d1e00c0db9010db86154a4ba6e91385", "parentId": "86154a4ba6e91385", "id": "4d1e00c0db9010db", "kind": "CLIENT", @@ -8,7 +8,7 @@ "duration": 207000, "localEndpoint": { "serviceName": "frontend", - "ipv4": "127.0.0.1" + "ipv6": "7::0.128.128.127" }, "remoteEndpoint": { "serviceName": "backend", diff --git a/zipkin-collector/kafka/src/test/java/zipkin/collector/kafka/KafkaCollectorTest.java b/zipkin-collector/kafka/src/test/java/zipkin/collector/kafka/KafkaCollectorTest.java index 6bee5115fd4..de2d0993ffa 100644 --- a/zipkin-collector/kafka/src/test/java/zipkin/collector/kafka/KafkaCollectorTest.java +++ b/zipkin-collector/kafka/src/test/java/zipkin/collector/kafka/KafkaCollectorTest.java @@ -32,8 +32,8 @@ import zipkin.collector.kafka.KafkaCollector.Builder; import zipkin.internal.ApplyTimestampAndDuration; import zipkin.internal.V2SpanConverter; -import zipkin.internal.v2.codec.MessageEncoder; -import zipkin.internal.v2.codec.Encoder; +import zipkin.internal.v2.codec.BytesMessageEncoder; +import zipkin.internal.v2.codec.BytesEncoder; import zipkin.storage.AsyncSpanConsumer; import zipkin.storage.AsyncSpanStore; import zipkin.storage.SpanStore; @@ -147,9 +147,9 @@ public void messageWithMultipleSpans_json2() throws Exception { ApplyTimestampAndDuration.apply(LOTS_OF_SPANS[1]) ); - byte[] message = MessageEncoder.JSON_BYTES.encode(asList( - Encoder.JSON.encode(V2SpanConverter.fromSpan(spans.get(0)).get(0)), - Encoder.JSON.encode(V2SpanConverter.fromSpan(spans.get(1)).get(0)) + byte[] message = BytesMessageEncoder.JSON_TO_BYTES.encode(asList( + BytesEncoder.JSON.encode(V2SpanConverter.fromSpan(spans.get(0)).get(0)), + BytesEncoder.JSON.encode(V2SpanConverter.fromSpan(spans.get(1)).get(0)) )); producer.send(new KeyedMessage<>(builder.topic, message)); diff --git a/zipkin-collector/kafka10/src/test/java/zipkin/collector/kafka10/KafkaCollectorTest.java b/zipkin-collector/kafka10/src/test/java/zipkin/collector/kafka10/KafkaCollectorTest.java index d5e8333a871..b0b625c7476 100644 --- a/zipkin-collector/kafka10/src/test/java/zipkin/collector/kafka10/KafkaCollectorTest.java +++ b/zipkin-collector/kafka10/src/test/java/zipkin/collector/kafka10/KafkaCollectorTest.java @@ -39,8 +39,8 @@ import zipkin.collector.kafka10.KafkaCollector.Builder; import zipkin.internal.ApplyTimestampAndDuration; import zipkin.internal.V2SpanConverter; -import zipkin.internal.v2.codec.MessageEncoder; -import zipkin.internal.v2.codec.Encoder; +import zipkin.internal.v2.codec.BytesMessageEncoder; +import zipkin.internal.v2.codec.BytesEncoder; import zipkin.storage.AsyncSpanConsumer; import zipkin.storage.AsyncSpanStore; import zipkin.storage.SpanStore; @@ -201,9 +201,9 @@ public void messageWithMultipleSpans_json2() throws Exception { ApplyTimestampAndDuration.apply(LOTS_OF_SPANS[1]) ); - byte[] message = MessageEncoder.JSON_BYTES.encode(asList( - Encoder.JSON.encode(V2SpanConverter.fromSpan(spans.get(0)).get(0)), - Encoder.JSON.encode(V2SpanConverter.fromSpan(spans.get(1)).get(0)) + byte[] message = BytesMessageEncoder.JSON_TO_BYTES.encode(asList( + BytesEncoder.JSON.encode(V2SpanConverter.fromSpan(spans.get(0)).get(0)), + BytesEncoder.JSON.encode(V2SpanConverter.fromSpan(spans.get(1)).get(0)) )); produceSpans(message, builder.topic); diff --git a/zipkin-junit/src/main/java/zipkin/junit/ZipkinDispatcher.java b/zipkin-junit/src/main/java/zipkin/junit/ZipkinDispatcher.java index 131c35cf0e0..0b2f332e1f3 100644 --- a/zipkin-junit/src/main/java/zipkin/junit/ZipkinDispatcher.java +++ b/zipkin-junit/src/main/java/zipkin/junit/ZipkinDispatcher.java @@ -32,7 +32,7 @@ import zipkin.collector.CollectorMetrics; import zipkin.internal.V2JsonSpanDecoder; import zipkin.internal.V2StorageComponent; -import zipkin.internal.v2.codec.Encoder; +import zipkin.internal.v2.codec.BytesEncoder; import zipkin.internal.v2.internal.Platform; import zipkin.storage.Callback; import zipkin.storage.QueryRequest; @@ -157,7 +157,7 @@ static void writeTrace(ByteArrayOutputStream bout, List throws IOException { bout.write('['); for (int i = 0, length = trace.size(); i < length; ) { - bout.write(Encoder.JSON.encode(trace.get(i))); + bout.write(BytesEncoder.JSON.encode(trace.get(i))); if (++i < length) bout.write(','); } bout.write(']'); diff --git a/zipkin-junit/src/test/java/zipkin/junit/ZipkinRuleTest.java b/zipkin-junit/src/test/java/zipkin/junit/ZipkinRuleTest.java index 0483037e66a..105d1bc2880 100644 --- a/zipkin-junit/src/test/java/zipkin/junit/ZipkinRuleTest.java +++ b/zipkin-junit/src/test/java/zipkin/junit/ZipkinRuleTest.java @@ -32,8 +32,8 @@ import zipkin.Span; import zipkin.internal.ApplyTimestampAndDuration; import zipkin.internal.V2SpanConverter; -import zipkin.internal.v2.codec.MessageEncoder; -import zipkin.internal.v2.codec.Encoder; +import zipkin.internal.v2.codec.BytesMessageEncoder; +import zipkin.internal.v2.codec.BytesEncoder; import static java.lang.String.format; import static java.util.Arrays.asList; @@ -69,9 +69,9 @@ public void getTraces_storedViaPostVersion2() throws IOException { ApplyTimestampAndDuration.apply(LOTS_OF_SPANS[1]) ); - byte[] message = MessageEncoder.JSON_BYTES.encode(asList( - Encoder.JSON.encode(V2SpanConverter.fromSpan(spans.get(0)).get(0)), - Encoder.JSON.encode(V2SpanConverter.fromSpan(spans.get(1)).get(0)) + byte[] message = BytesMessageEncoder.JSON_TO_BYTES.encode(asList( + BytesEncoder.JSON.encode(V2SpanConverter.fromSpan(spans.get(0)).get(0)), + BytesEncoder.JSON.encode(V2SpanConverter.fromSpan(spans.get(1)).get(0)) )); // write the span to the zipkin using http api v2 diff --git a/zipkin-junit/src/test/java/zipkin/junit/v2/HttpV2SpanConsumer.java b/zipkin-junit/src/test/java/zipkin/junit/v2/HttpV2SpanConsumer.java index 814fd6e2ab7..e264ba5bb6a 100644 --- a/zipkin-junit/src/test/java/zipkin/junit/v2/HttpV2SpanConsumer.java +++ b/zipkin-junit/src/test/java/zipkin/junit/v2/HttpV2SpanConsumer.java @@ -21,7 +21,7 @@ import okhttp3.RequestBody; import okio.Buffer; import zipkin.internal.v2.Span; -import zipkin.internal.v2.codec.Encoder; +import zipkin.internal.v2.codec.BytesEncoder; import zipkin.internal.v2.storage.SpanConsumer; /** Implements the span consumer interface by forwarding requests over http. */ @@ -36,7 +36,7 @@ final class HttpV2SpanConsumer implements SpanConsumer { Buffer json = new Buffer(); json.writeByte('['); for (int i = 0, length = spans.size(); i < length; ) { - json.write(Encoder.JSON.encode(spans.get(i))); + json.write(BytesEncoder.JSON.encode(spans.get(i))); if (++i < length) json.writeByte(','); } json.writeByte(']'); diff --git a/zipkin-junit/src/test/java/zipkin/junit/v2/HttpV2SpanStore.java b/zipkin-junit/src/test/java/zipkin/junit/v2/HttpV2SpanStore.java index ce85db731a9..4876fa6f771 100644 --- a/zipkin-junit/src/test/java/zipkin/junit/v2/HttpV2SpanStore.java +++ b/zipkin-junit/src/test/java/zipkin/junit/v2/HttpV2SpanStore.java @@ -23,7 +23,7 @@ import zipkin.DependencyLink; import zipkin.internal.v2.Call; import zipkin.internal.v2.Span; -import zipkin.internal.v2.codec.Decoder; +import zipkin.internal.v2.codec.BytesDecoder; import zipkin.internal.v2.storage.QueryRequest; import zipkin.internal.v2.storage.SpanStore; @@ -46,13 +46,13 @@ final class HttpV2SpanStore implements SpanStore { maybeAddQueryParam(url, "lookback", request.lookback()); maybeAddQueryParam(url, "limit", request.limit()); return factory.newCall(new Request.Builder().url(url.build()).build(), - content -> Decoder.JSON.decodeNestedList(content.readByteArray())); + content -> BytesDecoder.JSON.decodeNestedList(content.readByteArray())); } @Override public Call> getTrace(String traceId) { return factory.newCall(new Request.Builder() .url(factory.baseUrl.resolve("/api/v2/trace/" + Span.normalizeTraceId(traceId))) - .build(), content -> Decoder.JSON.decodeList(content.readByteArray())) + .build(), content -> BytesDecoder.JSON.decodeList(content.readByteArray())) .handleError(((error, callback) -> { if (error instanceof HttpException && ((HttpException) error).code == 404) { callback.onSuccess(Collections.emptyList()); diff --git a/zipkin-server/src/main/java/zipkin/server/ZipkinQueryApiV2.java b/zipkin-server/src/main/java/zipkin/server/ZipkinQueryApiV2.java index 7e019eabed2..c45ed2e9233 100644 --- a/zipkin-server/src/main/java/zipkin/server/ZipkinQueryApiV2.java +++ b/zipkin-server/src/main/java/zipkin/server/ZipkinQueryApiV2.java @@ -37,7 +37,7 @@ import zipkin.internal.V2StorageComponent; import zipkin.internal.v2.Call; import zipkin.internal.v2.Span; -import zipkin.internal.v2.codec.Encoder; +import zipkin.internal.v2.codec.BytesEncoder; import zipkin.internal.v2.storage.QueryRequest; import zipkin.storage.StorageComponent; @@ -132,7 +132,7 @@ public String getTraces( buffer.writeByte('['); List trace = traces.get(i); for (int j = 0, jLength = trace.size(); j < jLength; ) { - buffer.write(Encoder.JSON.encode(trace.get(j))); + buffer.write(BytesEncoder.JSON.encode(trace.get(j))); if (++j < jLength) buffer.writeByte(','); } buffer.writeByte(']'); @@ -151,7 +151,7 @@ public String getTrace(@PathVariable String traceIdHex, WebRequest request) thro Buffer buffer = new Buffer(); buffer.writeByte('['); for (int i = 0, length = trace.size(); i < length; ) { - buffer.write(Encoder.JSON.encode(trace.get(i))); + buffer.write(BytesEncoder.JSON.encode(trace.get(i))); if (++i < length) buffer.writeByte(','); } buffer.writeByte(']'); diff --git a/zipkin-server/src/test/java/zipkin/server/ZipkinServerIntegrationTest.java b/zipkin-server/src/test/java/zipkin/server/ZipkinServerIntegrationTest.java index 24b1c356d09..aac6cb38219 100644 --- a/zipkin-server/src/test/java/zipkin/server/ZipkinServerIntegrationTest.java +++ b/zipkin-server/src/test/java/zipkin/server/ZipkinServerIntegrationTest.java @@ -35,8 +35,8 @@ import zipkin.internal.ApplyTimestampAndDuration; import zipkin.internal.V2InMemoryStorage; import zipkin.internal.V2SpanConverter; -import zipkin.internal.v2.codec.Encoder; -import zipkin.internal.v2.codec.MessageEncoder; +import zipkin.internal.v2.codec.BytesEncoder; +import zipkin.internal.v2.codec.BytesMessageEncoder; import static java.lang.String.format; import static java.util.Arrays.asList; @@ -86,8 +86,8 @@ public void writeSpans_noContentTypeIsJson() throws Exception { public void writeSpans_version2() throws Exception { Span span = ApplyTimestampAndDuration.apply(LOTS_OF_SPANS[0]); - byte[] message = MessageEncoder.JSON_BYTES.encode(asList( - Encoder.JSON.encode(V2SpanConverter.fromSpan(span).get(0)) + byte[] message = BytesMessageEncoder.JSON_TO_BYTES.encode(asList( + BytesEncoder.JSON.encode(V2SpanConverter.fromSpan(span).get(0)) )); performAsync(post("/api/v2/spans").content(message)) diff --git a/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpSpanConsumer.java b/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpSpanConsumer.java index c4dc33f9c83..6526609111a 100644 --- a/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpSpanConsumer.java +++ b/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpSpanConsumer.java @@ -23,10 +23,10 @@ import javax.annotation.Nullable; import okio.Buffer; import okio.ByteString; -import zipkin.Annotation; +import zipkin.internal.v2.Annotation; import zipkin.internal.v2.Call; import zipkin.internal.v2.Span; -import zipkin.internal.v2.codec.Encoder; +import zipkin.internal.v2.codec.BytesEncoder; import zipkin.internal.v2.storage.SpanConsumer; import zipkin.storage.elasticsearch.http.internal.client.HttpCall; @@ -60,7 +60,7 @@ void indexSpans(BulkSpanIndexer indexer, List spans) { // guessTimestamp is made for determining the span's authoritative timestamp. When choosing // the index bucket, any annotation is better than using current time. for (int i = 0, length = span.annotations().size(); i < length; i++) { - indexTimestamp = span.annotations().get(i).timestamp / 1000; + indexTimestamp = span.annotations().get(i).timestamp() / 1000; break; } if (indexTimestamp == 0L) indexTimestamp = System.currentTimeMillis(); @@ -114,8 +114,8 @@ static byte[] prefixWithTimestampMillisAndQuery(Span span, @Nullable Long timest writer.name("_q"); writer.beginArray(); for (Annotation a : span.annotations()) { - if (a.value.length() > 255) continue; - writer.value(a.value); + if (a.value().length() > 255) continue; + writer.value(a.value()); } for (Map.Entry tag : span.tags().entrySet()) { if (tag.getKey().length() + tag.getValue().length() + 1 > 255) continue; @@ -131,9 +131,9 @@ static byte[] prefixWithTimestampMillisAndQuery(Span span, @Nullable Long timest if (LOG.isLoggable(Level.FINE)) { LOG.log(Level.FINE, "Error indexing query for span: " + span, e); } - return Encoder.JSON.encode(span); + return BytesEncoder.JSON.encode(span); } - byte[] document = Encoder.JSON.encode(span); + byte[] document = BytesEncoder.JSON.encode(span); if (query.rangeEquals(0L, ByteString.of(new byte[] {'{', '}'}))) { return document; } diff --git a/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/JsonAdapters.java b/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/JsonAdapters.java index c18aab32a62..d695e941081 100644 --- a/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/JsonAdapters.java +++ b/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/JsonAdapters.java @@ -13,15 +13,16 @@ */ package zipkin.storage.elasticsearch.http; +import com.google.gson.stream.MalformedJsonException; import com.squareup.moshi.JsonAdapter; import com.squareup.moshi.JsonReader; import com.squareup.moshi.JsonWriter; import java.io.IOException; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import zipkin.Annotation; import zipkin.DependencyLink; -import zipkin.Endpoint; +import zipkin.internal.v2.Annotation; +import zipkin.internal.v2.Endpoint; import zipkin.internal.v2.Span; /** @@ -32,7 +33,7 @@ final class JsonAdapters { static final JsonAdapter SPAN_ADAPTER = new JsonAdapter() { @Override @Nonnull public Span fromJson(JsonReader reader) throws IOException { - Span.Builder result = Span.builder(); + Span.Builder result = Span.newBuilder(); reader.beginObject(); while (reader.hasNext()) { String nextName = reader.nextName(); @@ -72,7 +73,7 @@ public Span fromJson(JsonReader reader) throws IOException { reader.beginArray(); while (reader.hasNext()) { Annotation a = ANNOTATION_ADAPTER.fromJson(reader); - result.addAnnotation(a.timestamp, a.value); + result.addAnnotation(a.timestamp(), a.value()); } reader.endArray(); break; @@ -104,27 +105,27 @@ public void toJson(JsonWriter writer, @Nullable Span value) throws IOException { }; static final JsonAdapter ANNOTATION_ADAPTER = new JsonAdapter() { - @Override @Nonnull - public Annotation fromJson(JsonReader reader) throws IOException { - Annotation.Builder result = Annotation.builder(); + @Override @Nonnull public Annotation fromJson(JsonReader reader) throws IOException { reader.beginObject(); + Long timestamp = null; + String value = null; while (reader.hasNext()) { switch (reader.nextName()) { case "timestamp": - result.timestamp(reader.nextLong()); + timestamp = reader.nextLong(); break; case "value": - result.value(reader.nextString()); - break; - case "endpoint": - result.endpoint(ENDPOINT_ADAPTER.fromJson(reader)); + value = reader.nextString(); break; default: reader.skipValue(); } } reader.endObject(); - return result.build(); + if (timestamp == null || value == null) { + throw new MalformedJsonException("Incomplete annotation at " + reader.getPath()); + } + return Annotation.create(timestamp, value); } @Override @@ -134,10 +135,10 @@ public void toJson(JsonWriter writer, @Nullable Annotation value) throws IOExcep }; static final JsonAdapter ENDPOINT_ADAPTER = new JsonAdapter() { - @Override @Nonnull - public Endpoint fromJson(JsonReader reader) throws IOException { - Endpoint.Builder result = Endpoint.builder().serviceName(""); + @Override @Nonnull public Endpoint fromJson(JsonReader reader) throws IOException { reader.beginObject(); + String serviceName = null, ipv4 = null, ipv6 = null; + Integer port = null; while (reader.hasNext()) { String nextName = reader.nextName(); if (reader.peek() == JsonReader.Token.NULL) { @@ -146,21 +147,26 @@ public Endpoint fromJson(JsonReader reader) throws IOException { } switch (nextName) { case "serviceName": - result.serviceName(reader.nextString()); + serviceName = reader.nextString(); break; case "ipv4": + ipv4 = reader.nextString(); + break; case "ipv6": - result.parseIp(reader.nextString()); + ipv6 = reader.nextString(); break; case "port": - result.port(reader.nextInt()); + port = reader.nextInt(); break; default: reader.skipValue(); } } reader.endObject(); - return result.build(); + if (serviceName == null && ipv4 == null && ipv6 == null && port == null) { + throw new MalformedJsonException("Incomplete endpoint at " + reader.getPath()); + } + return Endpoint.newBuilder().serviceName(serviceName).ip(ipv4).ip(ipv6).port(port).build(); } @Override diff --git a/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/LegacyJsonAdapters.java b/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/LegacyJsonAdapters.java index bf9471b9fbb..fc14b70c32e 100644 --- a/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/LegacyJsonAdapters.java +++ b/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/LegacyJsonAdapters.java @@ -18,16 +18,18 @@ import com.squareup.moshi.JsonReader; import com.squareup.moshi.JsonWriter; import java.io.IOException; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import okio.Buffer; import okio.ByteString; +import zipkin.Annotation; import zipkin.BinaryAnnotation; +import zipkin.Endpoint; import zipkin.Span; import zipkin.internal.Util; import static zipkin.internal.Util.UTF_8; import static zipkin.internal.Util.lowerHexToUnsignedLong; -import static zipkin.storage.elasticsearch.http.JsonAdapters.ENDPOINT_ADAPTER; final class LegacyJsonAdapters { static final JsonAdapter SPAN_ADAPTER = new JsonAdapter() { @@ -67,7 +69,7 @@ public Span fromJson(JsonReader reader) throws IOException { case "annotations": reader.beginArray(); while (reader.hasNext()) { - result.addAnnotation(JsonAdapters.ANNOTATION_ADAPTER.fromJson(reader)); + result.addAnnotation(ANNOTATION_ADAPTER.fromJson(reader)); } reader.endArray(); break; @@ -176,4 +178,70 @@ public void toJson(JsonWriter writer, @Nullable BinaryAnnotation value) throws I throw new UnsupportedOperationException(); } }; + + static final JsonAdapter ANNOTATION_ADAPTER = new JsonAdapter() { + @Override @Nonnull + public Annotation fromJson(JsonReader reader) throws IOException { + Annotation.Builder result = Annotation.builder(); + reader.beginObject(); + while (reader.hasNext()) { + switch (reader.nextName()) { + case "timestamp": + result.timestamp(reader.nextLong()); + break; + case "value": + result.value(reader.nextString()); + break; + case "endpoint": + result.endpoint(ENDPOINT_ADAPTER.fromJson(reader)); + break; + default: + reader.skipValue(); + } + } + reader.endObject(); + return result.build(); + } + + @Override + public void toJson(JsonWriter writer, @Nullable Annotation value) throws IOException { + throw new UnsupportedOperationException(); + } + }; + + static final JsonAdapter ENDPOINT_ADAPTER = new JsonAdapter() { + @Override @Nonnull + public Endpoint fromJson(JsonReader reader) throws IOException { + Endpoint.Builder result = Endpoint.builder().serviceName(""); + reader.beginObject(); + while (reader.hasNext()) { + String nextName = reader.nextName(); + if (reader.peek() == JsonReader.Token.NULL) { + reader.skipValue(); + continue; + } + switch (nextName) { + case "serviceName": + result.serviceName(reader.nextString()); + break; + case "ipv4": + case "ipv6": + result.parseIp(reader.nextString()); + break; + case "port": + result.port(reader.nextInt()); + break; + default: + reader.skipValue(); + } + } + reader.endObject(); + return result.build(); + } + + @Override + public void toJson(JsonWriter writer, @Nullable Endpoint value) throws IOException { + throw new UnsupportedOperationException(); + } + }.nullSafe(); } diff --git a/zipkin-storage/elasticsearch-http/src/test/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpSpanConsumerTest.java b/zipkin-storage/elasticsearch-http/src/test/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpSpanConsumerTest.java index e4cb83dcef1..690ee29c152 100644 --- a/zipkin-storage/elasticsearch-http/src/test/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpSpanConsumerTest.java +++ b/zipkin-storage/elasticsearch-http/src/test/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpSpanConsumerTest.java @@ -22,12 +22,12 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import zipkin.TestObjects; +import zipkin.internal.v2.Endpoint; import zipkin.internal.v2.Span; import zipkin.internal.v2.Span.Kind; -import zipkin.internal.v2.codec.Decoder; -import zipkin.internal.v2.codec.Encoder; -import zipkin.internal.v2.codec.MessageEncoder; +import zipkin.internal.v2.codec.BytesDecoder; +import zipkin.internal.v2.codec.BytesEncoder; +import zipkin.internal.v2.codec.BytesMessageEncoder; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; @@ -36,6 +36,9 @@ import static zipkin.storage.elasticsearch.http.ElasticsearchHttpSpanConsumer.prefixWithTimestampMillisAndQuery; public class ElasticsearchHttpSpanConsumerTest { + static final Endpoint WEB_ENDPOINT = Endpoint.newBuilder().serviceName("web").build(); + static final Endpoint APP_ENDPOINT = Endpoint.newBuilder().serviceName("app").build(); + @Rule public MockWebServer es = new MockWebServer(); ElasticsearchHttpStorage storage = ElasticsearchHttpStorage.builder() @@ -62,7 +65,7 @@ public void close() throws IOException { @Test public void addsTimestamp_millisIntoJson() throws Exception { es.enqueue(new MockResponse()); - Span span = Span.builder().traceId("20").id("20").name("get") + Span span = Span.newBuilder().traceId("20").id("20").name("get") .timestamp(TODAY * 1000).build(); accept(span); @@ -72,8 +75,8 @@ public void close() throws IOException { } @Test public void prefixWithTimestampMillisAndQuery_skipsWhenNoData() throws Exception { - Span span = Span.builder().traceId("20").id("22").name("").parentId("21").timestamp(0L) - .localEndpoint(TestObjects.WEB_ENDPOINT) + Span span = Span.newBuilder().traceId("20").id("22").name("").parentId("21").timestamp(0L) + .localEndpoint(WEB_ENDPOINT) .kind(Kind.CLIENT) .build(); @@ -84,8 +87,8 @@ public void close() throws IOException { } @Test public void prefixWithTimestampMillisAndQuery_addsTimestampMillis() throws Exception { - Span span = Span.builder().traceId("20").id("22").name("").parentId("21").timestamp(1L) - .localEndpoint(TestObjects.WEB_ENDPOINT) + Span span = Span.newBuilder().traceId("20").id("22").name("").parentId("21").timestamp(1L) + .localEndpoint(WEB_ENDPOINT) .kind(Kind.CLIENT) .build(); @@ -96,8 +99,8 @@ public void close() throws IOException { } @Test public void prefixWithTimestampMillisAndQuery_addsAnnotationQuery() throws Exception { - Span span = Span.builder().traceId("20").id("22").name("").parentId("21") - .localEndpoint(TestObjects.WEB_ENDPOINT) + Span span = Span.newBuilder().traceId("20").id("22").name("").parentId("21") + .localEndpoint(WEB_ENDPOINT) .addAnnotation(1L, "\"foo") .build(); @@ -108,8 +111,8 @@ public void close() throws IOException { } @Test public void prefixWithTimestampMillisAndQuery_addsAnnotationQueryTags() throws Exception { - Span span = Span.builder().traceId("20").id("22").name("").parentId("21") - .localEndpoint(TestObjects.WEB_ENDPOINT) + Span span = Span.newBuilder().traceId("20").id("22").name("").parentId("21") + .localEndpoint(WEB_ENDPOINT) .putTag("\"foo", "\"bar") .build(); @@ -120,21 +123,21 @@ public void close() throws IOException { } @Test public void prefixWithTimestampMillisAndQuery_readable() throws Exception { - Span span = Span.builder().traceId("20").id("20").name("get") + Span span = Span.newBuilder().traceId("20").id("20").name("get") .timestamp(TODAY * 1000).build(); - byte[] message = MessageEncoder.JSON_BYTES.encode(asList( + byte[] message = BytesMessageEncoder.JSON_TO_BYTES.encode(asList( prefixWithTimestampMillisAndQuery(span, span.timestamp()) )); - assertThat(Decoder.JSON.decodeList(message)) + assertThat(BytesDecoder.JSON.decodeList(message)) .containsOnly(span); // ignores timestamp_millis field } @Test public void doesntWriteDocumentId() throws Exception { es.enqueue(new MockResponse()); - accept(Span.builder().traceId("1").id("1").name("foo").build()); + accept(Span.newBuilder().traceId("1").id("1").name("foo").build()); RecordedRequest request = es.takeRequest(); assertThat(request.getBody().readByteString().utf8()) @@ -144,26 +147,26 @@ public void close() throws IOException { @Test public void writesSpanNaturallyWhenNoTimestamp() throws Exception { es.enqueue(new MockResponse()); - Span span = Span.builder().traceId("1").id("1").name("foo").build(); - accept(Span.builder().traceId("1").id("1").name("foo").build()); + Span span = Span.newBuilder().traceId("1").id("1").name("foo").build(); + accept(Span.newBuilder().traceId("1").id("1").name("foo").build()); assertThat(es.takeRequest().getBody().readByteString().utf8()) - .contains("\n" + new String(Encoder.JSON.encode(span), UTF_8) + "\n"); + .contains("\n" + new String(BytesEncoder.JSON.encode(span), UTF_8) + "\n"); } @Test public void traceIsSearchableByServerServiceName() throws Exception { es.enqueue(new MockResponse()); - Span clientSpan = Span.builder().traceId("20").id("22").name("").parentId("21") + Span clientSpan = Span.newBuilder().traceId("20").id("22").name("").parentId("21") .timestamp(1000L) .kind(Kind.CLIENT) - .localEndpoint(TestObjects.WEB_ENDPOINT) + .localEndpoint(WEB_ENDPOINT) .build(); - Span serverSpan = Span.builder().traceId("20").id("22").name("get").parentId("21") + Span serverSpan = Span.newBuilder().traceId("20").id("22").name("get").parentId("21") .timestamp(2000L) .kind(Kind.SERVER) - .localEndpoint(TestObjects.APP_ENDPOINT) + .localEndpoint(APP_ENDPOINT) .build(); accept(serverSpan, clientSpan); @@ -185,7 +188,7 @@ public void close() throws IOException { es.enqueue(new MockResponse()); - accept(Span.builder().traceId("1").id("1").name("foo").build()); + accept(Span.newBuilder().traceId("1").id("1").name("foo").build()); RecordedRequest request = es.takeRequest(); assertThat(request.getPath()) @@ -195,8 +198,8 @@ public void close() throws IOException { @Test public void choosesTypeSpecificIndex() throws Exception { es.enqueue(new MockResponse()); - Span span = Span.builder().traceId("1").id("2").parentId("1").name("s") - .localEndpoint(TestObjects.APP_ENDPOINT) + Span span = Span.newBuilder().traceId("1").id("2").parentId("1").name("s") + .localEndpoint(APP_ENDPOINT) .addAnnotation(TimeUnit.DAYS.toMicros(365) /* 1971-01-01 */, "foo") .build(); diff --git a/zipkin-storage/elasticsearch-http/src/test/java/zipkin/storage/elasticsearch/http/JsonAdaptersTest.java b/zipkin-storage/elasticsearch-http/src/test/java/zipkin/storage/elasticsearch/http/JsonAdaptersTest.java index d4a77cdc6b0..0d39dd62c51 100644 --- a/zipkin-storage/elasticsearch-http/src/test/java/zipkin/storage/elasticsearch/http/JsonAdaptersTest.java +++ b/zipkin-storage/elasticsearch-http/src/test/java/zipkin/storage/elasticsearch/http/JsonAdaptersTest.java @@ -18,12 +18,12 @@ import org.junit.Test; import zipkin.Codec; import zipkin.DependencyLink; -import zipkin.Endpoint; import zipkin.TestObjects; import zipkin.internal.ApplyTimestampAndDuration; import zipkin.internal.V2SpanConverter; +import zipkin.internal.v2.Endpoint; import zipkin.internal.v2.Span; -import zipkin.internal.v2.codec.Encoder; +import zipkin.internal.v2.codec.BytesEncoder; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; @@ -133,7 +133,7 @@ public void span_roundTrip() throws IOException { zipkin.Span span = ApplyTimestampAndDuration.apply(TestObjects.LOTS_OF_SPANS[0]); Span span2 = V2SpanConverter.fromSpan(span).get(0); Buffer bytes = new Buffer(); - bytes.write(Encoder.JSON.encode(span2)); + bytes.write(BytesEncoder.JSON.encode(span2)); assertThat(SPAN_ADAPTER.fromJson(bytes)) .isEqualTo(span2); } @@ -145,8 +145,8 @@ public void span_roundTrip() throws IOException { @Test public void span_specialCharsInJson() throws IOException { // service name is surrounded by control characters - Endpoint e = Endpoint.create(new String(new char[] {0, 'a', 1}), 0); - Span worstSpanInTheWorld = Span.builder().traceId("1").id("1") + Endpoint e = Endpoint.newBuilder().serviceName(new String(new char[] {0, 'a', 1})).build(); + Span worstSpanInTheWorld = Span.newBuilder().traceId("1").id("1") // name is terrible .name(new String(new char[] {'"', '\\', '\t', '\b', '\n', '\r', '\f'})) .localEndpoint(e) @@ -158,7 +158,7 @@ public void span_specialCharsInJson() throws IOException { .build(); Buffer bytes = new Buffer(); - bytes.write(Encoder.JSON.encode(worstSpanInTheWorld)); + bytes.write(BytesEncoder.JSON.encode(worstSpanInTheWorld)); assertThat(SPAN_ADAPTER.fromJson(bytes)) .isEqualTo(worstSpanInTheWorld); } @@ -176,7 +176,7 @@ public void span_endpointHighPort() throws IOException { + "}"; assertThat(SPAN_ADAPTER.fromJson(json).localEndpoint()) - .isEqualTo(Endpoint.builder().serviceName("service").port(65535).build()); + .isEqualTo(Endpoint.newBuilder().serviceName("service").port(65535).build()); } @Test @@ -191,7 +191,7 @@ public void span_noServiceName() throws IOException { + "}"; assertThat(SPAN_ADAPTER.fromJson(json).localEndpoint()) - .isEqualTo(Endpoint.builder().serviceName("").port(65535).build()); + .isEqualTo(Endpoint.newBuilder().serviceName("").port(65535).build()); } @Test @@ -207,7 +207,7 @@ public void span_nullServiceName() throws IOException { + "}"; assertThat(SPAN_ADAPTER.fromJson(json).localEndpoint()) - .isEqualTo(Endpoint.builder().serviceName("").port(65535).build()); + .isEqualTo(Endpoint.newBuilder().serviceName("").port(65535).build()); } @Test diff --git a/zipkin-storage/mysql/src/main/java/zipkin/storage/mysql/DependencyLinkV2SpanIterator.java b/zipkin-storage/mysql/src/main/java/zipkin/storage/mysql/DependencyLinkV2SpanIterator.java index a5d9c62a7e5..7ec9a1c3a7d 100644 --- a/zipkin-storage/mysql/src/main/java/zipkin/storage/mysql/DependencyLinkV2SpanIterator.java +++ b/zipkin-storage/mysql/src/main/java/zipkin/storage/mysql/DependencyLinkV2SpanIterator.java @@ -19,8 +19,8 @@ import org.jooq.TableField; import zipkin.BinaryAnnotation.Type; import zipkin.Constants; -import zipkin.Endpoint; import zipkin.internal.PeekingIterator; +import zipkin.internal.v2.Endpoint; import zipkin.internal.v2.Span; import zipkin.storage.mysql.internal.generated.tables.ZipkinSpans; @@ -133,7 +133,7 @@ public Span next() { if (equal(saService, caService)) caService = null; Long parentId = row.getValue(ZipkinSpans.ZIPKIN_SPANS.PARENT_ID); - Span.Builder result = Span.builder() + Span.Builder result = Span.newBuilder() .traceId(toLowerHex(traceIdHi != null ? traceIdHi : 0L, traceIdLo)) .parentId(parentId != null ? toLowerHex(parentId) : null) .id(toLowerHex(spanId)); @@ -176,6 +176,6 @@ static long traceIdHigh(PeekingIterator delegate) { } static Endpoint ep(@Nullable String serviceName) { - return serviceName != null ? Endpoint.builder().serviceName(serviceName).build() : null; + return serviceName != null ? Endpoint.newBuilder().serviceName(serviceName).build() : null; } } diff --git a/zipkin-storage/mysql/src/test/java/zipkin/storage/mysql/DependencyLinkV2SpanIteratorTest.java b/zipkin-storage/mysql/src/test/java/zipkin/storage/mysql/DependencyLinkV2SpanIteratorTest.java index 55e7be4159d..d404b142794 100644 --- a/zipkin-storage/mysql/src/test/java/zipkin/storage/mysql/DependencyLinkV2SpanIteratorTest.java +++ b/zipkin-storage/mysql/src/test/java/zipkin/storage/mysql/DependencyLinkV2SpanIteratorTest.java @@ -55,8 +55,8 @@ public class DependencyLinkV2SpanIteratorTest { Span span = iterator.next(); assertThat(span.kind()).isNull(); - assertThat(span.localEndpoint().serviceName).isEqualTo("service1"); - assertThat(span.remoteEndpoint().serviceName).isEqualTo("service2"); + assertThat(span.localServiceName()).isEqualTo("service1"); + assertThat(span.remoteServiceName()).isEqualTo("service2"); } /** The linker is biased towards server spans, or client spans that know the peer localEndpoint(). */ @@ -79,7 +79,7 @@ public class DependencyLinkV2SpanIteratorTest { Span span = iterator.next(); assertThat(span.kind()).isEqualTo(Span.Kind.SERVER); - assertThat(span.localEndpoint().serviceName).isEqualTo("service"); + assertThat(span.localServiceName()).isEqualTo("service"); assertThat(span.remoteEndpoint()).isNull(); } @@ -116,8 +116,8 @@ public class DependencyLinkV2SpanIteratorTest { Span span = iterator.next(); assertThat(span.kind()).isEqualTo(Span.Kind.SERVER); - assertThat(span.localEndpoint().serviceName).isEqualTo("service2"); - assertThat(span.remoteEndpoint().serviceName).isEqualTo("service1"); + assertThat(span.localServiceName()).isEqualTo("service2"); + assertThat(span.remoteServiceName()).isEqualTo("service1"); } /** @@ -132,8 +132,8 @@ public class DependencyLinkV2SpanIteratorTest { Span span = iterator.next(); assertThat(span.kind()).isEqualTo(Span.Kind.SERVER); - assertThat(span.localEndpoint().serviceName).isEqualTo("service2"); - assertThat(span.remoteEndpoint().serviceName).isEqualTo("service1"); + assertThat(span.localServiceName()).isEqualTo("service2"); + assertThat(span.remoteServiceName()).isEqualTo("service1"); } /** {@link Constants#CLIENT_ADDR} is more authoritative than {@link Constants#CLIENT_SEND} */ @@ -146,8 +146,8 @@ public class DependencyLinkV2SpanIteratorTest { Span span = iterator.next(); assertThat(span.kind()).isEqualTo(Span.Kind.SERVER); - assertThat(span.localEndpoint().serviceName).isEqualTo("service2"); - assertThat(span.remoteEndpoint().serviceName).isEqualTo("service1"); + assertThat(span.localServiceName()).isEqualTo("service2"); + assertThat(span.remoteServiceName()).isEqualTo("service1"); } /** Finagle labels two sides of the same socket "ca", Type.BOOL.value, "sa" with the local endpoint name */ @@ -162,7 +162,7 @@ public class DependencyLinkV2SpanIteratorTest { // When there's no "sr" annotation, we assume it is a client. assertThat(span.kind()).isEqualTo(Span.Kind.CLIENT); assertThat(span.localEndpoint()).isNull(); - assertThat(span.remoteEndpoint().serviceName).isEqualTo("service"); + assertThat(span.remoteServiceName()).isEqualTo("service"); } @Test public void specialCasesFinagleLocalSocketLabeling_server() { @@ -175,7 +175,7 @@ public class DependencyLinkV2SpanIteratorTest { // When there is an "sr" annotation, we know it is a server assertThat(span.kind()).isEqualTo(Span.Kind.SERVER); - assertThat(span.localEndpoint().serviceName).isEqualTo("service"); + assertThat(span.localServiceName()).isEqualTo("service"); assertThat(span.remoteEndpoint()).isNull(); } @@ -190,7 +190,7 @@ public class DependencyLinkV2SpanIteratorTest { Span span = iterator.next(); assertThat(span.kind()).isEqualTo(Span.Kind.SERVER); - assertThat(span.localEndpoint().serviceName).isEqualTo("service1"); + assertThat(span.localServiceName()).isEqualTo("service1"); assertThat(span.remoteEndpoint()).isNull(); } diff --git a/zipkin/src/main/java/zipkin/collector/Collector.java b/zipkin/src/main/java/zipkin/collector/Collector.java index 4f381f32fe3..7e4180a55ac 100644 --- a/zipkin/src/main/java/zipkin/collector/Collector.java +++ b/zipkin/src/main/java/zipkin/collector/Collector.java @@ -23,7 +23,7 @@ import zipkin.internal.V2SpanConverter; import zipkin.internal.V2StorageComponent; import zipkin.internal.v2.Span; -import zipkin.internal.v2.codec.Decoder; +import zipkin.internal.v2.codec.BytesDecoder; import zipkin.storage.Callback; import zipkin.storage.StorageComponent; @@ -108,7 +108,7 @@ public void acceptSpans(byte[] serializedSpans, SpanDecoder decoder, Callback value) { return writeList(STRING_WRITER, value); } - static T read(JsonReaderAdapter adapter, byte[] bytes) { + public static T read(JsonReaderAdapter adapter, byte[] bytes) { checkArgument(bytes.length > 0, "Empty input reading %s", adapter); try { return adapter.fromJson(jsonReader(bytes)); diff --git a/zipkin/src/main/java/zipkin/internal/V2Collector.java b/zipkin/src/main/java/zipkin/internal/V2Collector.java index 5f9e0c75a4c..f763a6e12f6 100644 --- a/zipkin/src/main/java/zipkin/internal/V2Collector.java +++ b/zipkin/src/main/java/zipkin/internal/V2Collector.java @@ -19,12 +19,12 @@ import zipkin.collector.CollectorMetrics; import zipkin.collector.CollectorSampler; import zipkin.internal.v2.Span; -import zipkin.internal.v2.codec.Decoder; +import zipkin.internal.v2.codec.BytesDecoder; import zipkin.storage.Callback; import static zipkin.internal.Util.checkNotNull; -public final class V2Collector extends Collector, Span> { +public final class V2Collector extends Collector, Span> { final V2StorageComponent storage; final CollectorSampler sampler; @@ -36,11 +36,11 @@ public V2Collector(Logger logger, @Nullable CollectorMetrics metrics, } @Override - public void acceptSpans(byte[] serializedSpans, Decoder decoder, Callback callback) { + public void acceptSpans(byte[] serializedSpans, BytesDecoder decoder, Callback callback) { super.acceptSpans(serializedSpans, decoder, callback); } - @Override protected List decodeList(Decoder decoder, byte[] serialized) { + @Override protected List decodeList(BytesDecoder decoder, byte[] serialized) { return decoder.decodeList(serialized); } diff --git a/zipkin/src/main/java/zipkin/internal/V2JsonSpanDecoder.java b/zipkin/src/main/java/zipkin/internal/V2JsonSpanDecoder.java index 60cc693514f..5a1fbba042d 100644 --- a/zipkin/src/main/java/zipkin/internal/V2JsonSpanDecoder.java +++ b/zipkin/src/main/java/zipkin/internal/V2JsonSpanDecoder.java @@ -18,7 +18,7 @@ import java.util.List; import zipkin.SpanDecoder; import zipkin.internal.v2.Span; -import zipkin.internal.v2.codec.Decoder; +import zipkin.internal.v2.codec.BytesDecoder; /** Decodes a span from zipkin v2 encoding */ public final class V2JsonSpanDecoder implements SpanDecoder { @@ -27,7 +27,7 @@ public final class V2JsonSpanDecoder implements SpanDecoder { } @Override public List readSpans(byte[] span) { - List span2s = Decoder.JSON.decodeList(span); + List span2s = BytesDecoder.JSON.decodeList(span); if (span2s.isEmpty()) return Collections.emptyList(); int length = span2s.size(); List result = new ArrayList<>(length); diff --git a/zipkin/src/main/java/zipkin/internal/V2SpanConverter.java b/zipkin/src/main/java/zipkin/internal/V2SpanConverter.java index 4b235926049..865b660313c 100644 --- a/zipkin/src/main/java/zipkin/internal/V2SpanConverter.java +++ b/zipkin/src/main/java/zipkin/internal/V2SpanConverter.java @@ -13,6 +13,8 @@ */ package zipkin.internal; +import java.net.Inet6Address; +import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; @@ -22,7 +24,7 @@ import zipkin.Annotation; import zipkin.BinaryAnnotation; import zipkin.Constants; -import zipkin.Endpoint; +import zipkin.internal.v2.Endpoint; import zipkin.internal.v2.Span; import zipkin.internal.v2.Span.Kind; @@ -107,7 +109,7 @@ void processAnnotations(zipkin.Span source) { if (closeEnough(cs.endpoint, sr.endpoint)) { client.kind(Kind.CLIENT); // fork a new span for the server side - server = newSpanBuilder(source, sr.endpoint).kind(Kind.SERVER); + server = newSpanBuilder(source, convert(sr.endpoint)).kind(Kind.SERVER); } else { server = forEndpoint(source, sr.endpoint); } @@ -148,7 +150,7 @@ void processAnnotations(zipkin.Span source) { if (closeEnough(ms.endpoint, mr.endpoint)) { producer.kind(Kind.PRODUCER); // fork a new span for the consumer side - consumer = newSpanBuilder(source, mr.endpoint).kind(Kind.CONSUMER); + consumer = newSpanBuilder(source, convert(mr.endpoint)).kind(Kind.CONSUMER); } else { consumer = forEndpoint(source, mr.endpoint); } @@ -186,7 +188,7 @@ void maybeTimestampDuration(zipkin.Span source, Annotation begin, @Nullable Anno } void processBinaryAnnotations(zipkin.Span source) { - Endpoint ca = null, sa = null, ma = null; + zipkin.Endpoint ca = null, sa = null, ma = null; for (int i = 0, length = source.binaryAnnotations.size(); i < length; i++) { BinaryAnnotation b = source.binaryAnnotations.get(i); if (b.type == BOOL) { @@ -231,40 +233,41 @@ void processBinaryAnnotations(zipkin.Span source) { } if (cs != null && sa != null && !closeEnough(sa, cs.endpoint)) { - forEndpoint(source, cs.endpoint).remoteEndpoint(sa); + forEndpoint(source, cs.endpoint).remoteEndpoint(convert(sa)); } if (sr != null && ca != null && !closeEnough(ca, sr.endpoint)) { - forEndpoint(source, sr.endpoint).remoteEndpoint(ca); + forEndpoint(source, sr.endpoint).remoteEndpoint(convert(ca)); } if (ms != null && ma != null && !closeEnough(ma, ms.endpoint)) { - forEndpoint(source, ms.endpoint).remoteEndpoint(ma); + forEndpoint(source, ms.endpoint).remoteEndpoint(convert(ma)); } if (mr != null && ma != null && !closeEnough(ma, mr.endpoint)) { - forEndpoint(source, mr.endpoint).remoteEndpoint(ma); + forEndpoint(source, mr.endpoint).remoteEndpoint(convert(ma)); } // special-case when we are missing core annotations, but we have both address annotations if ((cs == null && sr == null) && (ca != null && sa != null)) { - forEndpoint(source, ca).remoteEndpoint(sa); + forEndpoint(source, ca).remoteEndpoint(convert(sa)); } } - Span.Builder forEndpoint(zipkin.Span source, @Nullable Endpoint e) { + Span.Builder forEndpoint(zipkin.Span source, @Nullable zipkin.Endpoint e) { if (e == null) return spans.get(0); // allocate missing endpoint data to first span + Endpoint converted = convert(e); for (int i = 0, length = spans.size(); i < length; i++) { Span.Builder next = spans.get(i); Endpoint nextLocalEndpoint = next.localEndpoint(); if (nextLocalEndpoint == null) { - next.localEndpoint(e); + next.localEndpoint(converted); return next; - } else if (closeEnough(nextLocalEndpoint, e)) { + } else if (closeEnough(convert(nextLocalEndpoint), e)) { return next; } } - return newSpanBuilder(source, e); + return newSpanBuilder(source, converted); } Span.Builder newSpanBuilder(zipkin.Span source, Endpoint e) { @@ -284,12 +287,12 @@ List build() { } } - static boolean closeEnough(Endpoint left, Endpoint right) { + static boolean closeEnough(zipkin.Endpoint left, zipkin.Endpoint right) { return left.serviceName.equals(right.serviceName); } static Span.Builder newBuilder(zipkin.Span source) { - return Span.builder() + return Span.newBuilder() .traceId(source.traceIdString()) .parentId(source.parentId != null ? Util.toLowerHex(source.parentId) : null) .id(Util.toLowerHex(source.id)) @@ -311,25 +314,25 @@ public static zipkin.Span toSpan(Span in) { result.traceIdHigh(lowerHexToUnsignedLong(traceId, 0)); } - long timestamp = in.timestamp() == null ? 0L : in.timestamp(); - long duration = in.duration() == null ? 0L : in.duration(); - if (timestamp != 0L) { - result.timestamp(timestamp); - if (duration != 0L) result.duration(duration); + long startTs = in.timestamp() == null ? 0L : in.timestamp(); + Long endTs = in.duration() == null ? 0L : in.timestamp() + in.duration(); + if (startTs != 0L) { + result.timestamp(startTs); + result.duration(in.duration()); } + zipkin.Endpoint local = in.localEndpoint() != null ? convert(in.localEndpoint()) : null; + zipkin.Endpoint remote = in.remoteEndpoint() != null ? convert(in.remoteEndpoint()) : null; Kind kind = in.kind(); - Annotation cs = null, sr = null, ss = null, cr = null, ms = null, mr = null, ws = null, wr = - null; + Annotation + cs = null, sr = null, ss = null, cr = null, ms = null, mr = null, ws = null, wr = null; String remoteEndpointType = null; boolean wroteEndpoint = false; for (int i = 0, length = in.annotations().size(); i < length; i++) { - Annotation a = in.annotations().get(i); - if (in.localEndpoint() != null) { - a = a.toBuilder().endpoint(in.localEndpoint()).build(); - } + zipkin.internal.v2.Annotation input = in.annotations().get(i); + Annotation a = Annotation.create(input.timestamp(), input.value(), local); if (a.value.length() == 2) { if (a.value.equals(Constants.CLIENT_SEND)) { kind = Kind.CLIENT; @@ -369,39 +372,26 @@ public static zipkin.Span toSpan(Span in) { switch (kind) { case CLIENT: remoteEndpointType = Constants.SERVER_ADDR; - if (timestamp != 0L) { - cs = Annotation.create(timestamp, Constants.CLIENT_SEND, in.localEndpoint()); - } - if (duration != 0L) { - cr = Annotation.create(timestamp + duration, Constants.CLIENT_RECV, in.localEndpoint()); - } + if (startTs != 0L) cs = Annotation.create(startTs, Constants.CLIENT_SEND, local); + if (endTs != 0L) cr = Annotation.create(endTs, Constants.CLIENT_RECV, local); break; case SERVER: remoteEndpointType = Constants.CLIENT_ADDR; - if (timestamp != 0L) { - sr = Annotation.create(timestamp, Constants.SERVER_RECV, in.localEndpoint()); - } - if (duration != 0L) { - ss = Annotation.create(timestamp + duration, Constants.SERVER_SEND, in.localEndpoint()); - } + if (startTs != 0L) sr = Annotation.create(startTs, Constants.SERVER_RECV, local); + if (endTs != 0L) ss = Annotation.create(endTs, Constants.SERVER_SEND, local); break; case PRODUCER: remoteEndpointType = Constants.MESSAGE_ADDR; - if (timestamp != 0L) { - ms = Annotation.create(timestamp, Constants.MESSAGE_SEND, in.localEndpoint()); - } - if (duration != 0L) { - ws = Annotation.create(timestamp + duration, Constants.WIRE_SEND, in.localEndpoint()); - } + if (startTs != 0L) ms = Annotation.create(startTs, Constants.MESSAGE_SEND, local); + if (endTs != 0L) ws = Annotation.create(endTs, Constants.WIRE_SEND, local); break; case CONSUMER: remoteEndpointType = Constants.MESSAGE_ADDR; - if (timestamp != 0L && duration != 0L) { - wr = Annotation.create(timestamp, Constants.WIRE_RECV, in.localEndpoint()); - mr = - Annotation.create(timestamp + duration, Constants.MESSAGE_RECV, in.localEndpoint()); - } else if (timestamp != 0L) { - mr = Annotation.create(timestamp, Constants.MESSAGE_RECV, in.localEndpoint()); + if (startTs != 0L && endTs != 0L) { + wr = Annotation.create(startTs, Constants.WIRE_RECV, local); + mr = Annotation.create(endTs, Constants.MESSAGE_RECV, local); + } else if (startTs != 0L) { + mr = Annotation.create(startTs, Constants.MESSAGE_RECV, local); } break; default: @@ -411,8 +401,7 @@ public static zipkin.Span toSpan(Span in) { for (Map.Entry tag : in.tags().entrySet()) { wroteEndpoint = true; - result.addBinaryAnnotation( - BinaryAnnotation.create(tag.getKey(), tag.getValue(), in.localEndpoint())); + result.addBinaryAnnotation(BinaryAnnotation.create(tag.getKey(), tag.getValue(), local)); } if (cs != null @@ -432,23 +421,57 @@ public static zipkin.Span toSpan(Span in) { if (ms != null) result.addAnnotation(ms); if (mr != null) result.addAnnotation(mr); wroteEndpoint = true; - } else if (in.localEndpoint() != null && in.remoteEndpoint() != null) { + } else if (local != null && remote != null) { // special-case when we are missing core annotations, but we have both address annotations - result.addBinaryAnnotation(BinaryAnnotation.address(CLIENT_ADDR, in.localEndpoint())); + result.addBinaryAnnotation(BinaryAnnotation.address(CLIENT_ADDR, local)); wroteEndpoint = true; remoteEndpointType = SERVER_ADDR; } - if (remoteEndpointType != null && in.remoteEndpoint() != null) { - result.addBinaryAnnotation(BinaryAnnotation.address(remoteEndpointType, in.remoteEndpoint())); + if (remoteEndpointType != null && remote != null) { + result.addBinaryAnnotation(BinaryAnnotation.address(remoteEndpointType, remote)); } // don't report server-side timestamp on shared or incomplete spans if (Boolean.TRUE.equals(in.shared()) && sr != null) { result.timestamp(null).duration(null); } - if (in.localEndpoint() != null && !wroteEndpoint) { // create a dummy annotation - result.addBinaryAnnotation(BinaryAnnotation.create(LOCAL_COMPONENT, "", in.localEndpoint())); + if (local != null && !wroteEndpoint) { // create a dummy annotation + result.addBinaryAnnotation(BinaryAnnotation.create(LOCAL_COMPONENT, "", local)); + } + return result.build(); + } + + public static zipkin.internal.v2.Endpoint convert(zipkin.Endpoint input) { + zipkin.internal.v2.Endpoint.Builder result = zipkin.internal.v2.Endpoint.newBuilder() + .serviceName(input.serviceName) + .port(input.port != null ? input.port & 0xffff : null); + if (input.ipv4 != 0) { + result.parseIp(new StringBuilder() + .append(input.ipv4 >> 24 & 0xff).append('.') + .append(input.ipv4 >> 16 & 0xff).append('.') + .append(input.ipv4 >> 8 & 0xff).append('.') + .append(input.ipv4 & 0xff).toString()); + } + if (input.ipv6 != null) { + try { + result.parseIp(Inet6Address.getByAddress(input.ipv6)); + } catch (UnknownHostException e) { + throw new AssertionError(e); // ipv6 is fixed length, so shouldn't happen. + } + } + return result.build(); + } + + public static zipkin.Endpoint convert(Endpoint input) { + zipkin.Endpoint.Builder result = zipkin.Endpoint.builder() + .serviceName(input.serviceName() != null ? input.serviceName() : "") + .port(input.port() != null ? input.port() : 0); + if (input.ipv6() != null) { + result.parseIp(input.ipv6()); // parse first in case there's a mapped IP + } + if (input.ipv4() != null) { + result.parseIp(input.ipv4()); } return result.build(); } diff --git a/zipkin/src/main/java/zipkin/internal/V2SpanStoreAdapter.java b/zipkin/src/main/java/zipkin/internal/V2SpanStoreAdapter.java index f2f8ed8c3e3..c1731e3986e 100644 --- a/zipkin/src/main/java/zipkin/internal/V2SpanStoreAdapter.java +++ b/zipkin/src/main/java/zipkin/internal/V2SpanStoreAdapter.java @@ -29,6 +29,7 @@ import zipkin.storage.Callback; import static zipkin.internal.GroupByTraceId.TRACE_DESCENDING; +import static zipkin.internal.Util.sortedList; import static zipkin.internal.Util.toLowerHex; final class V2SpanStoreAdapter implements zipkin.storage.SpanStore, AsyncSpanStore { @@ -93,7 +94,7 @@ Call> getRawTraceCall(long traceIdHigh, long traceIdLow) { @Override public List getServiceNames() { try { - return delegate.getServiceNames().execute(); + return sortedList(delegate.getServiceNames().execute()); } catch (IOException e) { throw Platform.get().uncheckedIOException(e); } @@ -105,7 +106,7 @@ Call> getRawTraceCall(long traceIdHigh, long traceIdLow) { @Override public List getSpanNames(String serviceName) { try { - return delegate.getSpanNames(serviceName).execute(); + return sortedList(delegate.getSpanNames(serviceName).execute()); } catch (IOException e) { throw Platform.get().uncheckedIOException(e); } diff --git a/zipkin/src/main/java/zipkin/internal/v2/Annotation.java b/zipkin/src/main/java/zipkin/internal/v2/Annotation.java new file mode 100644 index 00000000000..05ca7d9ecf9 --- /dev/null +++ b/zipkin/src/main/java/zipkin/internal/v2/Annotation.java @@ -0,0 +1,57 @@ +/** + * Copyright 2015-2017 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.v2; + +import com.google.auto.value.AutoValue; +import java.io.Serializable; +import javax.annotation.concurrent.Immutable; + +/** + * Associates an event that explains latency with a timestamp. + * + *

Unlike log statements, annotations are often codes: Ex. {@link "cache.miss"}. + */ +@AutoValue +@Immutable +public abstract class Annotation implements Comparable, Serializable { // for Spark jobs + private static final long serialVersionUID = 0L; + + public static Annotation create(long timestamp, String value) { + return new AutoValue_Annotation(timestamp, value); + } + + /** + * Microseconds from epoch. + * + *

This value should be set directly by instrumentation, using the most precise value possible. + * For example, {@code gettimeofday} or multiplying {@link System#currentTimeMillis} by 1000. + */ + public abstract long timestamp(); + + /** + * Usually a short tag indicating an event, like {@code cache.miss} or {@code error} + */ + public abstract String value(); + + /** Compares by {@link #timestamp}, then {@link #value}. */ + @Override public int compareTo(Annotation that) { + if (this == that) return 0; + int byTimestamp = timestamp() < that.timestamp() ? -1 : timestamp() == that.timestamp() ? 0 : 1; + if (byTimestamp != 0) return byTimestamp; + return value().compareTo(that.value()); + } + + Annotation() { + } +} diff --git a/zipkin/src/main/java/zipkin/internal/v2/Endpoint.java b/zipkin/src/main/java/zipkin/internal/v2/Endpoint.java new file mode 100644 index 00000000000..27a72f931ac --- /dev/null +++ b/zipkin/src/main/java/zipkin/internal/v2/Endpoint.java @@ -0,0 +1,438 @@ +/** + * Copyright 2015-2017 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.v2; + +import com.google.auto.value.AutoValue; +import java.io.Serializable; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.nio.ByteBuffer; +import java.util.Locale; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** The network context of a node in the service graph. */ +@AutoValue +@Immutable +public abstract class Endpoint implements Serializable { // for Spark jobs + private static final long serialVersionUID = 0L; + + /** + * Lower-case label of this node in the service graph, such as "favstar". Leave absent if + * unknown. + * + *

This is a primary label for trace lookup and aggregation, so it should be intuitive and + * consistent. Many use a name from service discovery. + */ + @Nullable public abstract String serviceName(); + + /** + * The text representation of the primary IPv4 address associated with this a connection. Ex. + * 192.168.99.100 Absent if unknown. + */ + @Nullable public abstract String ipv4(); + + /** + * The text representation of the primary IPv6 address associated with this a connection. Ex. + * 2001:db8::c001 Absent if unknown. + * + *

Prefer using the {@link #ipv4()} field for mapped addresses. + */ + @Nullable public abstract String ipv6(); + + /** + * Port of the IP's socket or null, if not known. + * + * @see java.net.InetSocketAddress#getPort() + */ + @Nullable public abstract Integer port(); + + public abstract Builder toBuilder(); + + public static Builder newBuilder() { + return new AutoValue_Endpoint.Builder(); + } + + @AutoValue.Builder + public static abstract class Builder { + /** @see Endpoint#serviceName */ + public abstract Builder serviceName(@Nullable String serviceName); + + /** Chaining variant of {@link #parseIp(InetAddress)} */ + public Builder ip(@Nullable InetAddress addr) { + parseIp(addr); + return this; + } + + /** + * Returns true if {@link #ipv4(String)} or {@link #ipv6(String)} could be parsed from the + * input. + * + *

Returns boolean not this for conditional parsing. For example: + *

{@code
+     * if (!builder.parseIp(input.getHeader("X-Forwarded-For"))) {
+     *   builder.parseIp(input.getRemoteAddr());
+     * }
+     * }
+ * + * @see #parseIp(String) + */ + public final boolean parseIp(@Nullable InetAddress addr) { + if (addr == null) return false; + if (addr instanceof Inet4Address) { + ipv4(addr.getHostAddress()); + } else if (addr instanceof Inet6Address) { + byte[] addressBytes = addr.getAddress(); + String ipv4 = parseEmbeddedIPv4(addressBytes); + if (ipv4 != null) { + ipv4(ipv4); + } else { + ipv6(writeIpV6(addressBytes)); + } + } else { + return false; + } + return true; + } + + /** Chaining variant of {@link #parseIp(String)} */ + public Builder ip(@Nullable String ipString) { + parseIp(ipString); + return this; + } + + /** + * Returns true if {@link #ipv4(String)} or {@link #ipv6(String)} could be parsed from the + * input. + * + *

Returns boolean not this for conditional parsing. For example: + *

{@code
+     * if (!builder.parseIp(input.getHeader("X-Forwarded-For"))) {
+     *   builder.parseIp(input.getRemoteAddr());
+     * }
+     * }
+ * + * @see #parseIp(InetAddress) + */ + public final boolean parseIp(@Nullable String ipString) { + if (ipString == null || ipString.isEmpty()) return false; + IpFamily format = detectFamily(ipString); + if (format == IpFamily.IPv4) { + ipv4(ipString); + } else if (format == IpFamily.IPv4Embedded) { + ipv4(ipString.substring(ipString.lastIndexOf(':') + 1)); + } else if (format == IpFamily.IPv6) { + byte[] addressBytes = textToNumericFormatV6(ipString); + if (addressBytes == null) return false; + ipv6(writeIpV6(addressBytes)); // ensures consistent format + } else { + return false; + } + return true; + } + + // hidden to ensure input are parsed as otherwise equals comparisons like needed for clock skew + // won't work. + + /** @see Endpoint#ipv4 */ + abstract Builder ipv4(@Nullable String ipv4); + + /** @see Endpoint#ipv4 */ + abstract Builder ipv6(@Nullable String ipv6); + + /** + * Use this to set the port to an externally defined value. + * + * @param port port associated with the endpoint. zero coerces to null (unknown) + * @see Endpoint#port() + */ + public abstract Builder port(@Nullable Integer port); + + abstract @Nullable String serviceName(); + + abstract @Nullable Integer port(); + + abstract Endpoint autoBuild(); + + public final Endpoint build() { + String serviceName = serviceName(); + if (serviceName != null) { + if (serviceName.isEmpty()) { + serviceName(null); + } else { + serviceName(serviceName.toLowerCase(Locale.ROOT)); + } + } + Integer port = port(); + if (port != null) { + if (port < 0 || port > 0xffff) throw new IllegalArgumentException("invalid port " + port); + if (port == 0) port(null); + } + return autoBuild(); + } + + Builder() { + } + } + + static @Nullable String parseEmbeddedIPv4(byte[] ipv6) { + for (int i = 0; i < 10; i++) { // Embedded IPv4 addresses start with unset 80 bits + if (ipv6[i] != 0) return null; + } + + int flag = (ipv6[10] & 0xff) << 8 | (ipv6[11] & 0xff); + if (flag != 0 && flag != -1) return null; // IPv4-Compatible or IPv4-Mapped + + int o1 = ipv6[12] & 0xff, o2 = ipv6[13] & 0xff, o3 = ipv6[14] & 0xff, o4 = ipv6[15] & 0xff; + if (flag == 0 && o1 == 0 && o2 == 0 && o3 == 0 && o4 == 1) { + return null; // ::1 is localhost, not an embedded compat address + } + + return String.valueOf(o1) + '.' + o2 + '.' + o3 + '.' + o4; + } + + enum IpFamily { + Unknown, + IPv4, + IPv4Embedded, + IPv6 + } + + /** + * Adapted from code in {@code com.google.common.net.InetAddresses.ipStringToBytes}. This version + * separates detection from parsing and checks more carefully about embedded addresses. + */ + static IpFamily detectFamily(String ipString) { + boolean hasColon = false; + boolean hasDot = false; + for (int i = 0, length = ipString.length(); i < length; i++) { + char c = ipString.charAt(i); + if (c == '.') { + hasDot = true; + } else if (c == ':') { + if (hasDot) return IpFamily.Unknown; // Colons must not appear after dots. + hasColon = true; + } else if (notHex(c)) { + return IpFamily.Unknown; // Everything else must be a decimal or hex digit. + } + } + + // Now decide which address family to parse. + if (hasColon) { + if (hasDot) { + int lastColon = ipString.lastIndexOf(':'); + if (!isValidIpV4Address(ipString, lastColon + 1, ipString.length())) { + return IpFamily.Unknown; + } + if (lastColon == 1 && ipString.charAt(0) == ':') {// compressed like ::1.2.3.4 + return IpFamily.IPv4Embedded; + } + if (lastColon != 6 || ipString.charAt(0) != ':' || ipString.charAt(1) != ':') { + return IpFamily.Unknown; + } + for (int i = 2; i < 6; i++) { + char c = ipString.charAt(i); + if (c != 'f' && c != 'F' && c != '0') return IpFamily.Unknown; + } + return IpFamily.IPv4Embedded; + } + return IpFamily.IPv6; + } else if (hasDot && isValidIpV4Address(ipString, 0, ipString.length())) { + return IpFamily.IPv4; + } + return IpFamily.Unknown; + } + + private static boolean notHex(char c) { + return (c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F'); + } + + private static final ThreadLocal IPV6_TO_STRING = new ThreadLocal() { + @Override protected char[] initialValue() { + return new char[39]; // maximum length of encoded ipv6 + } + }; + + static String writeIpV6(byte[] ipv6) { + int pos = 0; + char[] buf = IPV6_TO_STRING.get(); + + // Compress the longest string of zeros + int zeroCompressionIndex = -1; + int zeroCompressionLength = -1; + int zeroIndex = -1; + boolean allZeros = true; + for (int i = 0; i < ipv6.length; i += 2) { + if (ipv6[i] == 0 && ipv6[i + 1] == 0) { + if (zeroIndex < 0) zeroIndex = i; + continue; + } + allZeros = false; + if (zeroIndex >= 0) { + int zeroLength = i - zeroIndex; + if (zeroLength > zeroCompressionLength) { + zeroCompressionIndex = zeroIndex; + zeroCompressionLength = zeroLength; + } + zeroIndex = -1; + } + } + + // handle all zeros: 0:0:0:0:0:0:0:0 -> :: + if (allZeros) return "::"; + + // handle trailing zeros: 2001:0:0:4:0:0:0:0 -> 2001:0:0:4:: + if (zeroCompressionIndex == -1 && zeroIndex != -1) { + zeroCompressionIndex = zeroIndex; + zeroCompressionLength = 16 - zeroIndex; + } + + int i = 0; + while (i < ipv6.length) { + if (i == zeroCompressionIndex) { + buf[pos++] = ':'; + i += zeroCompressionLength; + if (i == ipv6.length) buf[pos++] = ':'; + continue; + } + if (i != 0) buf[pos++] = ':'; + + byte high = ipv6[i++]; + byte low = ipv6[i++]; + + // handle leading zeros: 2001:0:0:4:0000:0:0:8 -> 2001:0:0:4::8 + boolean leadingZero; + char val = HEX_DIGITS[(high >> 4) & 0xf]; + if (!(leadingZero = val == '0')) buf[pos++] = val; + val = HEX_DIGITS[high & 0xf]; + if (!(leadingZero = (leadingZero && val == '0'))) buf[pos++] = val; + val = HEX_DIGITS[(low >> 4) & 0xf]; + if (!(leadingZero && val == '0')) buf[pos++] = val; + buf[pos++] = HEX_DIGITS[low & 0xf]; + } + return new String(buf, 0, pos); + } + + static final char[] HEX_DIGITS = + {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + // Begin code from com.google.common.net.InetAddresses 23 + private static final int IPV6_PART_COUNT = 8; + + @Nullable + private static byte[] textToNumericFormatV6(String ipString) { + // An address can have [2..8] colons, and N colons make N+1 parts. + String[] parts = ipString.split(":", IPV6_PART_COUNT + 2); + if (parts.length < 3 || parts.length > IPV6_PART_COUNT + 1) { + return null; + } + + // Disregarding the endpoints, find "::" with nothing in between. + // This indicates that a run of zeroes has been skipped. + int skipIndex = -1; + for (int i = 1; i < parts.length - 1; i++) { + if (parts[i].length() == 0) { + if (skipIndex >= 0) { + return null; // Can't have more than one :: + } + skipIndex = i; + } + } + + int partsHi; // Number of parts to copy from above/before the "::" + int partsLo; // Number of parts to copy from below/after the "::" + if (skipIndex >= 0) { + // If we found a "::", then check if it also covers the endpoints. + partsHi = skipIndex; + partsLo = parts.length - skipIndex - 1; + if (parts[0].length() == 0 && --partsHi != 0) { + return null; // ^: requires ^:: + } + if (parts[parts.length - 1].length() == 0 && --partsLo != 0) { + return null; // :$ requires ::$ + } + } else { + // Otherwise, allocate the entire address to partsHi. The endpoints + // could still be empty, but parseHextet() will check for that. + partsHi = parts.length; + partsLo = 0; + } + + // If we found a ::, then we must have skipped at least one part. + // Otherwise, we must have exactly the right number of parts. + int partsSkipped = IPV6_PART_COUNT - (partsHi + partsLo); + if (!(skipIndex >= 0 ? partsSkipped >= 1 : partsSkipped == 0)) { + return null; + } + + // Now parse the hextets into a byte array. + ByteBuffer rawBytes = ByteBuffer.allocate(2 * IPV6_PART_COUNT); + try { + for (int i = 0; i < partsHi; i++) { + rawBytes.putShort(parseHextet(parts[i])); + } + for (int i = 0; i < partsSkipped; i++) { + rawBytes.putShort((short) 0); + } + for (int i = partsLo; i > 0; i--) { + rawBytes.putShort(parseHextet(parts[parts.length - i])); + } + } catch (NumberFormatException ex) { + return null; + } + return rawBytes.array(); + } + + private static short parseHextet(String ipPart) { + // Note: we already verified that this string contains only hex digits. + int hextet = Integer.parseInt(ipPart, 16); + if (hextet > 0xffff) { + throw new NumberFormatException(); + } + return (short) hextet; + } + // End code from com.google.common.net.InetAddresses 23 + + // Begin code from io.netty.util.NetUtil 4.1 + private static boolean isValidIpV4Address(String ip, int from, int toExcluded) { + int len = toExcluded - from; + int i; + return len <= 15 && len >= 7 && + (i = ip.indexOf('.', from + 1)) > 0 && isValidIpV4Word(ip, from, i) && + (i = ip.indexOf('.', from = i + 2)) > 0 && isValidIpV4Word(ip, from - 1, i) && + (i = ip.indexOf('.', from = i + 2)) > 0 && isValidIpV4Word(ip, from - 1, i) && + isValidIpV4Word(ip, i + 1, toExcluded); + } + + private static boolean isValidIpV4Word(CharSequence word, int from, int toExclusive) { + int len = toExclusive - from; + char c0, c1, c2; + if (len < 1 || len > 3 || (c0 = word.charAt(from)) < '0') { + return false; + } + if (len == 3) { + return (c1 = word.charAt(from + 1)) >= '0' && + (c2 = word.charAt(from + 2)) >= '0' && + (c0 <= '1' && c1 <= '9' && c2 <= '9' || + c0 == '2' && c1 <= '5' && (c2 <= '5' || c1 < '5' && c2 <= '9')); + } + return c0 <= '9' && (len == 1 || isValidNumericChar(word.charAt(from + 1))); + } + + private static boolean isValidNumericChar(char c) { + return c >= '0' && c <= '9'; + } + // End code from io.netty.util.NetUtil 4.1 +} diff --git a/zipkin/src/main/java/zipkin/internal/v2/Span.java b/zipkin/src/main/java/zipkin/internal/v2/Span.java index 43ef0a588fe..d95a8ee17e7 100644 --- a/zipkin/src/main/java/zipkin/internal/v2/Span.java +++ b/zipkin/src/main/java/zipkin/internal/v2/Span.java @@ -15,7 +15,9 @@ import com.google.auto.value.AutoValue; import java.io.Serializable; +import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -24,15 +26,9 @@ import java.util.TreeMap; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; -import zipkin.Annotation; import zipkin.Constants; -import zipkin.Endpoint; import zipkin.TraceKeys; -import zipkin.internal.v2.codec.Encoder; - -import static zipkin.internal.Util.UTF_8; -import static zipkin.internal.Util.checkNotNull; -import static zipkin.internal.Util.sortedList; +import zipkin.internal.v2.codec.BytesEncoder; /** * A trace is a series of spans (often RPC calls) which form a latency tree. @@ -56,6 +52,8 @@ @AutoValue @Immutable public abstract class Span implements Serializable { // for Spark jobs + static final Charset UTF_8 = Charset.forName("UTF-8"); + private static final long serialVersionUID = 0L; /** @@ -166,7 +164,13 @@ public enum Kind { // Nullable for data conversion especially late arriving data which might not have an annotation @Nullable public abstract Endpoint localEndpoint(); - /** When an RPC (or messaging) span, indicates the other side of the connection. */ + /** + * When an RPC (or messaging) span, indicates the other side of the connection. + * + *

By recording the remote endpoint, your trace will contain network context even if the peer + * is not tracing. For example,For example, you can record the IP from the {@code X-Forwarded-For} + * header or or the service name and socket of a remote peer. + */ @Nullable public abstract Endpoint remoteEndpoint(); /** @@ -198,19 +202,15 @@ public enum Kind { @Nullable public String localServiceName() { Endpoint localEndpoint = localEndpoint(); - return localEndpoint != null && !"".equals(localEndpoint.serviceName) - ? localEndpoint.serviceName - : null; + return localEndpoint != null ? localEndpoint.serviceName() : null; } @Nullable public String remoteServiceName() { Endpoint remoteEndpoint = remoteEndpoint(); - return remoteEndpoint != null && !"".equals(remoteEndpoint.serviceName) - ? remoteEndpoint.serviceName - : null; + return remoteEndpoint != null ? remoteEndpoint.serviceName() : null; } - public static Builder builder() { + public static Builder newBuilder() { return new Builder(); } @@ -379,14 +379,16 @@ public Builder remoteEndpoint(@Nullable Endpoint remoteEndpoint) { /** @see Span#annotations */ public Builder addAnnotation(long timestamp, String value) { if (annotations == null) annotations = new ArrayList<>(2); - annotations.add(Annotation.create(timestamp, value, null)); + annotations.add(Annotation.create(timestamp, value)); return this; } /** @see Span#tags */ public Builder putTag(String key, String value) { if (tags == null) tags = new TreeMap<>(); - this.tags.put(checkNotNull(key, "key"), checkNotNull(value, "value")); + if (key == null) throw new NullPointerException("key == null"); + if (value == null) throw new NullPointerException("value of " + key + " == null"); + this.tags.put(key, value); return this; } @@ -425,7 +427,7 @@ public Span build() { } @Override public String toString() { - return new String(Encoder.JSON.encode(this), UTF_8); + return new String(BytesEncoder.JSON.encode(this), UTF_8); } /** @@ -464,4 +466,13 @@ static void validateHex(String id) { } } } + + static > List sortedList(@Nullable List in) { + if (in == null || in.isEmpty()) return Collections.emptyList(); + if (in.size() == 1) return Collections.singletonList(in.get(0)); + Object[] array = in.toArray(); + Arrays.sort(array); + List result = Arrays.asList(array); + return Collections.unmodifiableList(result); + } } diff --git a/zipkin/src/main/java/zipkin/internal/v2/codec/Decoder.java b/zipkin/src/main/java/zipkin/internal/v2/codec/BytesDecoder.java similarity index 60% rename from zipkin/src/main/java/zipkin/internal/v2/codec/Decoder.java rename to zipkin/src/main/java/zipkin/internal/v2/codec/BytesDecoder.java index 8e7586bb37a..dc09b746648 100644 --- a/zipkin/src/main/java/zipkin/internal/v2/codec/Decoder.java +++ b/zipkin/src/main/java/zipkin/internal/v2/codec/BytesDecoder.java @@ -20,26 +20,33 @@ /** * @param type of the span, usually {@link zipkin.Span} */ -public interface Decoder { - Decoder JSON = new Decoder() { +public interface BytesDecoder { + BytesDecoder JSON = new BytesDecoder() { @Override public Encoding encoding() { return Encoding.JSON; } - @Override public List decodeList(byte[] span) { - return JsonCodec.readList(new Span2JsonAdapters.Span2Reader(), span); + @Override public Span decode(byte[] span) { // ex decode span in dependencies job + return JsonCodec.read(new Span2JsonAdapters.Span2Reader(), span); } - @Override public List> decodeNestedList(byte[] span) { - return JsonCodec.readList(new Span2JsonAdapters.Span2ListReader(), span); + @Override public List decodeList(byte[] spans) { // ex getTrace + return JsonCodec.readList(new Span2JsonAdapters.Span2Reader(), spans); + } + + @Override public List> decodeNestedList(byte[] traces) { // ex getTraces + return JsonCodec.readList(new Span2JsonAdapters.Span2ListReader(), traces); } }; Encoding encoding(); - /** throws {@linkplain IllegalArgumentException} if the spans couldn't be decoded */ - List decodeList(byte[] span); + /** throws {@linkplain IllegalArgumentException} if the span couldn't be decoded */ + S decode(byte[] span); /** throws {@linkplain IllegalArgumentException} if the spans couldn't be decoded */ - List> decodeNestedList(byte[] span); + List decodeList(byte[] spans); + + /** throws {@linkplain IllegalArgumentException} if the traces couldn't be decoded */ + List> decodeNestedList(byte[] traces); } diff --git a/zipkin/src/main/java/zipkin/internal/v2/codec/Encoder.java b/zipkin/src/main/java/zipkin/internal/v2/codec/BytesEncoder.java similarity index 93% rename from zipkin/src/main/java/zipkin/internal/v2/codec/Encoder.java rename to zipkin/src/main/java/zipkin/internal/v2/codec/BytesEncoder.java index 5f0d9c53f2c..9b71aaf46e5 100644 --- a/zipkin/src/main/java/zipkin/internal/v2/codec/Encoder.java +++ b/zipkin/src/main/java/zipkin/internal/v2/codec/BytesEncoder.java @@ -21,8 +21,8 @@ /** * @param type of the span, usually {@link zipkin.Span} */ -public interface Encoder { - Encoder JSON = new Encoder() { +public interface BytesEncoder { + BytesEncoder JSON = new BytesEncoder() { @Override public Encoding encoding() { return Encoding.JSON; } diff --git a/zipkin/src/main/java/zipkin/internal/v2/codec/MessageEncoder.java b/zipkin/src/main/java/zipkin/internal/v2/codec/BytesMessageEncoder.java similarity index 88% rename from zipkin/src/main/java/zipkin/internal/v2/codec/MessageEncoder.java rename to zipkin/src/main/java/zipkin/internal/v2/codec/BytesMessageEncoder.java index 24af2fcbb09..2a5f414344e 100644 --- a/zipkin/src/main/java/zipkin/internal/v2/codec/MessageEncoder.java +++ b/zipkin/src/main/java/zipkin/internal/v2/codec/BytesMessageEncoder.java @@ -19,8 +19,8 @@ * Senders like Kafka use byte[] message encoding. This provides helpers to concatenate spans into a * list. */ -public interface MessageEncoder { - MessageEncoder JSON_BYTES = new MessageEncoder() { +public interface BytesMessageEncoder { + BytesMessageEncoder JSON_TO_BYTES = new BytesMessageEncoder() { @Override public Encoding encoding() { return Encoding.JSON; } @@ -47,7 +47,7 @@ public interface MessageEncoder { * comma-separated and enclosed by brackets. * *

The primary use of this is batch reporting spans. For example, spans are {@link - * Encoder#encode(Object) encoded} one-by-one into a queue. This queue is drained up to a byte + * BytesEncoder#encode(Object) encoded} one-by-one into a queue. This queue is drained up to a byte * threshold. Then, the list is encoded with this function and reported out-of-process. */ M encode(List encodedSpans); diff --git a/zipkin/src/main/java/zipkin/internal/v2/codec/Span2JsonAdapters.java b/zipkin/src/main/java/zipkin/internal/v2/codec/Span2JsonAdapters.java index 3c5881bf946..f11c51c369a 100644 --- a/zipkin/src/main/java/zipkin/internal/v2/codec/Span2JsonAdapters.java +++ b/zipkin/src/main/java/zipkin/internal/v2/codec/Span2JsonAdapters.java @@ -22,15 +22,14 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import zipkin.Annotation; -import zipkin.Endpoint; import zipkin.internal.Buffer; import zipkin.internal.JsonCodec; import zipkin.internal.JsonCodec.JsonReaderAdapter; +import zipkin.internal.v2.Annotation; +import zipkin.internal.v2.Endpoint; import zipkin.internal.v2.Span; import static zipkin.internal.Buffer.asciiSizeInBytes; -import static zipkin.internal.Buffer.ipv6SizeInBytes; import static zipkin.internal.Buffer.jsonEscapedSizeInBytes; /** @@ -44,7 +43,7 @@ static final class Span2Reader implements JsonReaderAdapter { @Override public Span fromJson(JsonReader reader) throws IOException { if (builder == null) { - builder = Span.builder(); + builder = Span.newBuilder(); } else { builder.clear(); } @@ -128,7 +127,7 @@ static final class Span2Reader implements JsonReaderAdapter { } static final JsonReaderAdapter ENDPOINT_READER = reader -> { - Endpoint.Builder result = Endpoint.builder().serviceName(""); + Endpoint.Builder result = Endpoint.newBuilder(); reader.beginObject(); boolean readField = false; while (reader.hasNext()) { @@ -157,55 +156,52 @@ static final class Span2Reader implements JsonReaderAdapter { static final Buffer.Writer ENDPOINT_WRITER = new Buffer.Writer() { @Override public int sizeInBytes(Endpoint value) { - int sizeInBytes = 1; // start curly-brace - if (!value.serviceName.isEmpty()) { - sizeInBytes += "\"serviceName\":\"".length(); - sizeInBytes += jsonEscapedSizeInBytes(value.serviceName) + 1; // for end quote - } - if (value.ipv4 != 0) { - if (sizeInBytes != 1) sizeInBytes++;// comma - sizeInBytes += "\"ipv4\":\"".length(); - sizeInBytes += asciiSizeInBytes(value.ipv4 >> 24 & 0xff) + 1; // for dot - sizeInBytes += asciiSizeInBytes(value.ipv4 >> 16 & 0xff) + 1; // for dot - sizeInBytes += asciiSizeInBytes(value.ipv4 >> 8 & 0xff) + 1; // for dot - sizeInBytes += asciiSizeInBytes(value.ipv4 & 0xff) + 1; // for end quote - } - if (value.ipv6 != null) { - if (sizeInBytes != 1) sizeInBytes++;// comma - sizeInBytes += "\"ipv6\":\"".length() + ipv6SizeInBytes(value.ipv6) + 1; - } - if (value.port != null && value.port != 0) { - if (sizeInBytes != 1) sizeInBytes++;// comma - sizeInBytes += "\"port\":".length() + asciiSizeInBytes(value.port & 0xffff); - } - return ++sizeInBytes;// end curly-brace + int sizeInBytes = 1; // { + if (value.serviceName() != null) { + sizeInBytes += 16; // "serviceName":"" + sizeInBytes += jsonEscapedSizeInBytes(value.serviceName()); + } + if (value.ipv4() != null) { + if (sizeInBytes != 1) sizeInBytes++; // , + sizeInBytes += 9; // "ipv4":"" + sizeInBytes += value.ipv4().length(); + } + if (value.ipv6() != null) { + if (sizeInBytes != 1) sizeInBytes++; // , + sizeInBytes += 9; // "ipv6":"" + sizeInBytes += value.ipv6().length(); + } + if (value.port() != null) { + if (sizeInBytes != 1) sizeInBytes++; // , + sizeInBytes += 7; // "port": + sizeInBytes += asciiSizeInBytes(value.port()); + } + return ++sizeInBytes; // } } @Override public void write(Endpoint value, Buffer b) { b.writeByte('{'); boolean wroteField = false; - if (!value.serviceName.isEmpty()) { + if (value.serviceName() != null) { b.writeAscii("\"serviceName\":\""); - b.writeJsonEscaped(value.serviceName).writeByte('"'); + b.writeJsonEscaped(value.serviceName()).writeByte('"'); wroteField = true; } - if (value.ipv4 != 0) { + if (value.ipv4() != null) { if (wroteField) b.writeByte(','); b.writeAscii("\"ipv4\":\""); - b.writeAscii(value.ipv4 >> 24 & 0xff).writeByte('.'); - b.writeAscii(value.ipv4 >> 16 & 0xff).writeByte('.'); - b.writeAscii(value.ipv4 >> 8 & 0xff).writeByte('.'); - b.writeAscii(value.ipv4 & 0xff).writeByte('"'); + b.writeAscii(value.ipv4()).writeByte('"'); wroteField = true; } - if (value.ipv6 != null) { + if (value.ipv6() != null) { if (wroteField) b.writeByte(','); - b.writeAscii("\"ipv6\":\"").writeIpV6(value.ipv6).writeByte('"'); + b.writeAscii("\"ipv6\":\""); + b.writeAscii(value.ipv6()).writeByte('"'); wroteField = true; } - if (value.port != null && value.port != 0) { + if (value.port() != null) { if (wroteField) b.writeByte(','); - b.writeAscii("\"port\":").writeAscii(value.port & 0xffff); + b.writeAscii("\"port\":").writeAscii(value.port()); } b.writeByte('}'); } @@ -213,63 +209,61 @@ static final class Span2Reader implements JsonReaderAdapter { static final Buffer.Writer SPAN_WRITER = new Buffer.Writer() { @Override public int sizeInBytes(Span value) { - int sizeInBytes = 0; - sizeInBytes += "{\"traceId\":\"".length() + value.traceId().length() + 1; + int sizeInBytes = 13; // {"traceId":"" + sizeInBytes += value.traceId().length(); if (value.parentId() != null) { - sizeInBytes += ",\"parentId\":\"".length() + 16 + 1; + sizeInBytes += 30; // ,"parentId":"0123456789abcdef" } - sizeInBytes += ",\"id\":\"".length() + 16 + 1; + sizeInBytes += 24; // ,"id":"0123456789abcdef" if (value.kind() != null) { - sizeInBytes += ",\"kind\":\"".length(); - sizeInBytes += value.kind().toString().length() + 1; + sizeInBytes += 10; // ,"kind":"" + sizeInBytes += value.kind().name().length(); } if (value.name() != null) { - sizeInBytes += ",\"name\":\"".length(); - sizeInBytes += jsonEscapedSizeInBytes(value.name()) + 1; + sizeInBytes += 10; // ,"name":"" + sizeInBytes += jsonEscapedSizeInBytes(value.name()); } if (value.timestamp() != null) { - sizeInBytes += ",\"timestamp\":".length(); + sizeInBytes += 13; // ,"timestamp": sizeInBytes += asciiSizeInBytes(value.timestamp()); } if (value.duration() != null) { - sizeInBytes += ",\"duration\":".length(); + sizeInBytes += 12; // ,"duration": sizeInBytes += asciiSizeInBytes(value.duration()); } if (value.localEndpoint() != null) { - sizeInBytes += ",\"localEndpoint\":".length(); + sizeInBytes += 17; // ,"localEndpoint": sizeInBytes += ENDPOINT_WRITER.sizeInBytes(value.localEndpoint()); } if (value.remoteEndpoint() != null) { - sizeInBytes += ",\"remoteEndpoint\":".length(); + sizeInBytes += 18; // ,"remoteEndpoint": sizeInBytes += ENDPOINT_WRITER.sizeInBytes(value.remoteEndpoint()); } if (!value.annotations().isEmpty()) { - sizeInBytes += ",\"annotations\":".length(); + sizeInBytes += 17; // ,"annotations":[] int length = value.annotations().size(); - sizeInBytes += 2; // brackets if (length > 1) sizeInBytes += length - 1; // comma to join elements for (int i = 0; i < length; i++) { sizeInBytes += ANNOTATION_WRITER.sizeInBytes(value.annotations().get(i)); } } if (!value.tags().isEmpty()) { - sizeInBytes += ",\"tags\":".length(); - sizeInBytes += 2; // curly braces + sizeInBytes += 10; // ,"tags":{} int tagCount = value.tags().size(); if (tagCount > 1) sizeInBytes += tagCount - 1; // comma to join elements for (Map.Entry entry : value.tags().entrySet()) { - sizeInBytes += 5; // 4 quotes and a colon + sizeInBytes += 5; // "":"" sizeInBytes += jsonEscapedSizeInBytes(entry.getKey()); sizeInBytes += jsonEscapedSizeInBytes(entry.getValue()); } } if (Boolean.TRUE.equals(value.debug())) { - sizeInBytes += ",\"debug\":true".length(); + sizeInBytes += 13; // ,"debug":true } if (Boolean.TRUE.equals(value.shared())) { - sizeInBytes += ",\"shared\":true".length(); + sizeInBytes += 14; // ,"shared":true } - return ++sizeInBytes;// end curly-brace + return ++sizeInBytes; // } } @Override public void write(Span value, Buffer b) { @@ -329,15 +323,15 @@ static final class Span2Reader implements JsonReaderAdapter { static final Buffer.Writer ANNOTATION_WRITER = new Buffer.Writer() { @Override public int sizeInBytes(Annotation value) { - int sizeInBytes = 0; - sizeInBytes += "{\"timestamp\":".length() + asciiSizeInBytes(value.timestamp); - sizeInBytes += ",\"value\":\"".length() + jsonEscapedSizeInBytes(value.value) + 1; - return ++sizeInBytes;// end curly-brace + int sizeInBytes = 25; // {"timestamp":,"value":""} + sizeInBytes += asciiSizeInBytes(value.timestamp()); + sizeInBytes += jsonEscapedSizeInBytes(value.value()); + return sizeInBytes; } @Override public void write(Annotation value, Buffer b) { - b.writeAscii("{\"timestamp\":").writeAscii(value.timestamp); - b.writeAscii(",\"value\":\"").writeJsonEscaped(value.value).writeAscii("\"}"); + b.writeAscii("{\"timestamp\":").writeAscii(value.timestamp()); + b.writeAscii(",\"value\":\"").writeJsonEscaped(value.value()).writeAscii("\"}"); } }; diff --git a/zipkin/src/main/java/zipkin/internal/v2/storage/InMemoryStorage.java b/zipkin/src/main/java/zipkin/internal/v2/storage/InMemoryStorage.java index a2d85cb08a3..29b215ab20a 100644 --- a/zipkin/src/main/java/zipkin/internal/v2/storage/InMemoryStorage.java +++ b/zipkin/src/main/java/zipkin/internal/v2/storage/InMemoryStorage.java @@ -13,6 +13,7 @@ */ package zipkin.internal.v2.storage; +import com.google.auto.value.AutoValue; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -29,13 +30,9 @@ import java.util.TreeMap; import zipkin.DependencyLink; import zipkin.internal.DependencyLinker; -import zipkin.internal.Pair; -import zipkin.internal.Util; import zipkin.internal.v2.Call; import zipkin.internal.v2.Span; -import static zipkin.internal.Util.sortedList; - /** * Test storage component that keeps all spans in memory, accepting them on the calling thread. * @@ -100,17 +97,17 @@ public InMemoryStorage build() { * other maps are derived from the span values here. This uses a list for the spans, so that it is * visible (via /api/v2/trace/id?raw) when instrumentation report the same spans multiple times. */ - private final SortedMultimap, Span> spansByTraceIdTimeStamp = - new SortedMultimap(VALUE_2_DESCENDING) { + private final SortedMultimap spansByTraceIdTimeStamp = + new SortedMultimap(TIMESTAMP_DESCENDING) { @Override Collection valueContainer() { return new LinkedList<>(); } }; /** This supports span lookup by {@link Span#traceId lower 64-bits of the trace ID} */ - private final SortedMultimap> traceIdToTraceIdTimeStamps = - new SortedMultimap>(Long::compareTo) { - @Override Collection> valueContainer() { + private final SortedMultimap traceIdToTraceIdTimeStamps = + new SortedMultimap(String::compareTo) { + @Override Collection valueContainer() { return new LinkedHashSet<>(); } }; @@ -147,25 +144,36 @@ public synchronized void clear() { evictToRecoverSpans(spansToRecover); for (Span span : spans) { Long timestamp = span.timestamp() != null ? span.timestamp() : Long.MIN_VALUE; - long traceId = Util.lowerHexToUnsignedLong(span.traceId()); - Pair traceIdTimeStamp = Pair.create(traceId, timestamp); + String lowTraceId = lowTraceId(span.traceId()); + TraceIdTimestamp traceIdTimeStamp = TraceIdTimestamp.create(lowTraceId, timestamp); spansByTraceIdTimeStamp.put(traceIdTimeStamp, span); - traceIdToTraceIdTimeStamps.put(traceId, traceIdTimeStamp); + traceIdToTraceIdTimeStamps.put(lowTraceId, traceIdTimeStamp); acceptedSpanCount++; String spanName = span.name(); if (span.localServiceName() != null) { - serviceToTraceIds.put(span.localServiceName(), traceId); + serviceToTraceIds.put(span.localServiceName(), lowTraceId); serviceToSpanNames.put(span.localServiceName(), spanName); } if (span.remoteServiceName() != null) { - serviceToTraceIds.put(span.remoteServiceName(), traceId); + serviceToTraceIds.put(span.remoteServiceName(), lowTraceId); serviceToSpanNames.put(span.remoteServiceName(), spanName); } } return Call.create(null /* Void == null */); } + @AutoValue + static abstract class TraceIdTimestamp { + static TraceIdTimestamp create(String traceId, long timestamp) { + return new AutoValue_InMemoryStorage_TraceIdTimestamp(traceId, timestamp); + } + + abstract String lowTraceId(); + + abstract long timestamp(); + } + /** Returns the count of spans evicted. */ int evictToRecoverSpans(int spansToRecover) { int spansEvicted = 0; @@ -180,15 +188,15 @@ int evictToRecoverSpans(int spansToRecover) { /** Returns the count of spans evicted. */ private int deleteOldestTrace() { int spansEvicted = 0; - long traceId = spansByTraceIdTimeStamp.delegate.lastKey()._1; - Collection> traceIdTimeStamps = traceIdToTraceIdTimeStamps.remove(traceId); - for (Iterator> traceIdTimeStampIter = traceIdTimeStamps.iterator(); + String lowTraceId = spansByTraceIdTimeStamp.delegate.lastKey().lowTraceId(); + Collection traceIdTimeStamps = traceIdToTraceIdTimeStamps.remove(lowTraceId); + for (Iterator traceIdTimeStampIter = traceIdTimeStamps.iterator(); traceIdTimeStampIter.hasNext(); ) { - Pair traceIdTimeStamp = traceIdTimeStampIter.next(); + TraceIdTimestamp traceIdTimeStamp = traceIdTimeStampIter.next(); Collection spans = spansByTraceIdTimeStamp.remove(traceIdTimeStamp); spansEvicted += spans.size(); } - for (String orphanedService : serviceToTraceIds.removeServiceIfTraceId(traceId)) { + for (String orphanedService : serviceToTraceIds.removeServiceIfTraceId(lowTraceId)) { serviceToSpanNames.remove(orphanedService); } return spansEvicted; @@ -200,13 +208,13 @@ public synchronized Call>> getTraces(QueryRequest request) { } synchronized Call>> getTraces(QueryRequest request, boolean strictTraceId) { - Set traceIdsInTimerange = traceIdsDescendingByTimestamp(request); + Set traceIdsInTimerange = traceIdsDescendingByTimestamp(request); if (traceIdsInTimerange.isEmpty()) return Call.emptyList(); List> result = new ArrayList<>(); - for (Iterator traceId = traceIdsInTimerange.iterator(); - traceId.hasNext() && result.size() < request.limit(); ) { - List next = spansByTraceId(traceId.next()); + for (Iterator lowTraceId = traceIdsInTimerange.iterator(); + lowTraceId.hasNext() && result.size() < request.limit(); ) { + List next = spansByTraceId(lowTraceId.next()); if (!request.test(next)) continue; if (!strictTraceId) { result.add(next); @@ -237,8 +245,8 @@ static Collection> strictByTraceId(List next) { /** Used for testing. Returns all traces unconditionally. */ public synchronized List> getTraces() { List> result = new ArrayList<>(); - for (long traceId : traceIdToTraceIdTimeStamps.keySet()) { - List sameTraceId = spansByTraceId(traceId); + for (String lowTraceId : traceIdToTraceIdTimeStamps.keySet()) { + List sameTraceId = spansByTraceId(lowTraceId); if (strictTraceId) { result.addAll(strictByTraceId(sameTraceId)); } else { @@ -248,8 +256,8 @@ public synchronized List> getTraces() { return result; } - Set traceIdsDescendingByTimestamp(QueryRequest request) { - Collection> traceIdTimestamps = request.serviceName() != null + Set traceIdsDescendingByTimestamp(QueryRequest request) { + Collection traceIdTimestamps = request.serviceName() != null ? traceIdTimestampsByServiceName(request.serviceName()) : spansByTraceIdTimeStamp.keySet(); @@ -257,10 +265,10 @@ Set traceIdsDescendingByTimestamp(QueryRequest request) { long startTs = endTs - request.lookback() * 1000; if (traceIdTimestamps == null || traceIdTimestamps.isEmpty()) return Collections.emptySet(); - Set result = new LinkedHashSet<>(); - for (Pair traceIdTimestamp : traceIdTimestamps) { - if (traceIdTimestamp._2 >= startTs || traceIdTimestamp._2 <= endTs) { - result.add(traceIdTimestamp._1); + Set result = new LinkedHashSet<>(); + for (TraceIdTimestamp traceIdTimestamp : traceIdTimestamps) { + if (traceIdTimestamp.timestamp() >= startTs || traceIdTimestamp.timestamp() <= endTs) { + result.add(traceIdTimestamp.lowTraceId()); } } return result; @@ -268,7 +276,7 @@ Set traceIdsDescendingByTimestamp(QueryRequest request) { @Override public synchronized Call> getTrace(String traceId) { traceId = Span.normalizeTraceId(traceId); - List spans = spansByTraceId(Util.lowerHexToUnsignedLong(traceId)); + List spans = spansByTraceId(lowTraceId(traceId)); if (spans == null || spans.isEmpty()) return Call.emptyList(); if (!strictTraceId) return Call.create(spans); @@ -283,15 +291,13 @@ Set traceIdsDescendingByTimestamp(QueryRequest request) { } @Override public synchronized Call> getServiceNames() { - List result = sortedList(serviceToTraceIds.keySet()); - return Call.create(result); + return Call.create(new ArrayList<>(serviceToTraceIds.keySet())); } @Override public synchronized Call> getSpanNames(String service) { if (service.isEmpty()) return Call.emptyList(); service = service.toLowerCase(Locale.ROOT); // service names are always lowercase! - List result = sortedList(serviceToSpanNames.get(service)); - return Call.create(result); + return Call.create(new ArrayList<>(serviceToSpanNames.get(service))); } @Override @@ -314,27 +320,28 @@ public synchronized Call> getDependencies(long endTs, long }); } - static final Comparator> VALUE_2_DESCENDING = (left, right) -> { - int result = right._2.compareTo(left._2); - if (result != 0) return result; - return right._1.compareTo(left._1); + static final Comparator TIMESTAMP_DESCENDING = (left, right) -> { + long x = left.timestamp(), y = right.timestamp(); + int result = (x < y) ? -1 : ((x == y) ? 0 : 1); + if (result != 0) return -result; // use negative as we are descending + return right.lowTraceId().compareTo(left.lowTraceId()); }; - static final class ServiceNameToTraceIds extends SortedMultimap { + static final class ServiceNameToTraceIds extends SortedMultimap { ServiceNameToTraceIds() { super(String::compareTo); } - @Override Set valueContainer() { + @Override Set valueContainer() { return new LinkedHashSet<>(); } /** Returns service names orphaned by removing the trace ID */ - Set removeServiceIfTraceId(long traceId) { + Set removeServiceIfTraceId(String lowTraceId) { Set result = new LinkedHashSet<>(); - for (Map.Entry> entry : delegate.entrySet()) { - Collection traceIds = entry.getValue(); - if (traceIds.remove(traceId) && traceIds.isEmpty()) { + for (Map.Entry> entry : delegate.entrySet()) { + Collection lowTraceIds = entry.getValue(); + if (lowTraceIds.remove(lowTraceId) && lowTraceIds.isEmpty()) { result.add(entry.getKey()); } } @@ -387,20 +394,24 @@ Collection get(K key) { } } - private List spansByTraceId(long lowTraceId) { + private List spansByTraceId(String lowTraceId) { List sameTraceId = new ArrayList<>(); - for (Pair traceIdTimestamp : traceIdToTraceIdTimeStamps.get(lowTraceId)) { + for (TraceIdTimestamp traceIdTimestamp : traceIdToTraceIdTimeStamps.get(lowTraceId)) { sameTraceId.addAll(spansByTraceIdTimeStamp.get(traceIdTimestamp)); } return sameTraceId; } - private Collection> traceIdTimestampsByServiceName(String serviceName) { - List> traceIdTimestamps = new ArrayList<>(); - for (long traceId : serviceToTraceIds.get(serviceName)) { - traceIdTimestamps.addAll(traceIdToTraceIdTimeStamps.get(traceId)); + private Collection traceIdTimestampsByServiceName(String serviceName) { + List traceIdTimestamps = new ArrayList<>(); + for (String lowTraceId : serviceToTraceIds.get(serviceName)) { + traceIdTimestamps.addAll(traceIdToTraceIdTimeStamps.get(lowTraceId)); } - Collections.sort(traceIdTimestamps, VALUE_2_DESCENDING); + Collections.sort(traceIdTimestamps, TIMESTAMP_DESCENDING); return traceIdTimestamps; } + + static String lowTraceId(String traceId) { + return traceId.length() == 32 ? traceId.substring(16) :traceId; + } } diff --git a/zipkin/src/main/java/zipkin/internal/v2/storage/QueryRequest.java b/zipkin/src/main/java/zipkin/internal/v2/storage/QueryRequest.java index d521b861fc4..9ec190fc36b 100644 --- a/zipkin/src/main/java/zipkin/internal/v2/storage/QueryRequest.java +++ b/zipkin/src/main/java/zipkin/internal/v2/storage/QueryRequest.java @@ -23,8 +23,8 @@ import java.util.Map; import java.util.Set; import javax.annotation.Nullable; -import zipkin.Annotation; -import zipkin.Endpoint; +import zipkin.internal.v2.Annotation; +import zipkin.internal.v2.Endpoint; import zipkin.internal.v2.Span; /** @@ -237,14 +237,14 @@ timestamp > endTs() * 1000) { for (Span span : spans) { Endpoint localEndpoint = span.localEndpoint(); - String localServiceName = localEndpoint != null ? localEndpoint.serviceName : null; + String localServiceName = span.localServiceName(); if (localServiceName != null) serviceNames.add(localServiceName); if (serviceName() == null || serviceName().equals(localServiceName)) { for (Annotation a : span.annotations()) { - if ("".equals(annotationQueryRemaining.get(a.value))) { - annotationQueryRemaining.remove(a.value); + if ("".equals(annotationQueryRemaining.get(a.value()))) { + annotationQueryRemaining.remove(a.value()); } } for (Map.Entry t : span.tags().entrySet()) { diff --git a/zipkin/src/test/java/zipkin/collector/CollectorTest.java b/zipkin/src/test/java/zipkin/collector/CollectorTest.java index 52e396e352f..988db32d709 100644 --- a/zipkin/src/test/java/zipkin/collector/CollectorTest.java +++ b/zipkin/src/test/java/zipkin/collector/CollectorTest.java @@ -22,8 +22,8 @@ import zipkin.internal.V2SpanConverter; import zipkin.internal.V2StorageComponent; import zipkin.internal.v2.Span; -import zipkin.internal.v2.codec.Encoder; -import zipkin.internal.v2.codec.MessageEncoder; +import zipkin.internal.v2.codec.BytesEncoder; +import zipkin.internal.v2.codec.BytesMessageEncoder; import zipkin.internal.v2.storage.SpanConsumer; import static java.util.Arrays.asList; @@ -85,7 +85,7 @@ public class CollectorTest { } @Test public void convertsSpan2Format() { - byte[] bytes = MessageEncoder.JSON_BYTES.encode(asList(Encoder.JSON.encode(span2_1))); + byte[] bytes = BytesMessageEncoder.JSON_TO_BYTES.encode(asList(BytesEncoder.JSON.encode(span2_1))); collector.acceptSpans(bytes, SpanDecoder.DETECTING_DECODER, NOOP); verify(collector).acceptSpans(bytes, SpanDecoder.DETECTING_DECODER, NOOP); @@ -107,7 +107,7 @@ abstract class WithSpan2 extends V2StorageComponent implements zipkin.storage.St collector = spy(Collector.builder(Collector.class) .storage(storage).build()); - byte[] bytes = MessageEncoder.JSON_BYTES.encode(asList(Encoder.JSON.encode(span2_1))); + byte[] bytes = BytesMessageEncoder.JSON_TO_BYTES.encode(asList(BytesEncoder.JSON.encode(span2_1))); collector.acceptSpans(bytes, SpanDecoder.DETECTING_DECODER, NOOP); verify(collector, never()).isSampled(any(zipkin.Span.class)); // skips v1 processing diff --git a/zipkin/src/test/java/zipkin/internal/DependencyLinkerTest.java b/zipkin/src/test/java/zipkin/internal/DependencyLinkerTest.java index 0c764bc50f4..d884577fcba 100644 --- a/zipkin/src/test/java/zipkin/internal/DependencyLinkerTest.java +++ b/zipkin/src/test/java/zipkin/internal/DependencyLinkerTest.java @@ -21,8 +21,8 @@ import javax.annotation.Nullable; import org.junit.Test; import zipkin.DependencyLink; -import zipkin.Endpoint; import zipkin.TestObjects; +import zipkin.internal.v2.Endpoint; import zipkin.internal.v2.Span; import zipkin.internal.v2.Span.Kind; @@ -555,13 +555,13 @@ public void merge_error() { static Span span2(long traceId, @Nullable Long parentId, long id, @Nullable Kind kind, @Nullable String local, @Nullable String remote, boolean isError) { - Span.Builder result = Span.builder() + Span.Builder result = Span.newBuilder() .traceId(toLowerHex(traceId)) .parentId(parentId != null ? toLowerHex(parentId) : null) .id(toLowerHex(id)) .kind(kind); - if (local != null) result.localEndpoint(Endpoint.builder().serviceName(local).build()); - if (remote != null) result.remoteEndpoint(Endpoint.builder().serviceName(remote).build()); + if (local != null) result.localEndpoint(Endpoint.newBuilder().serviceName(local).build()); + if (remote != null) result.remoteEndpoint(Endpoint.newBuilder().serviceName(remote).build()); if (isError) result.putTag(ERROR, ""); return result.build(); } diff --git a/zipkin/src/test/java/zipkin/internal/DetectingSpanDecoderTest.java b/zipkin/src/test/java/zipkin/internal/DetectingSpanDecoderTest.java index 6b402d4fcb3..547a6cc36e0 100644 --- a/zipkin/src/test/java/zipkin/internal/DetectingSpanDecoderTest.java +++ b/zipkin/src/test/java/zipkin/internal/DetectingSpanDecoderTest.java @@ -19,8 +19,8 @@ import zipkin.Codec; import zipkin.SpanDecoder; import zipkin.internal.v2.Span; -import zipkin.internal.v2.codec.Encoder; -import zipkin.internal.v2.codec.MessageEncoder; +import zipkin.internal.v2.codec.BytesEncoder; +import zipkin.internal.v2.codec.BytesMessageEncoder; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; @@ -72,16 +72,16 @@ public class DetectingSpanDecoderTest { /** Single-element reads were for legacy non-list encoding. Don't add new code that does this */ @Test(expected = UnsupportedOperationException.class) public void readSpan_json2() { - decoder.readSpan(Encoder.JSON.encode(span2_1)); + decoder.readSpan(BytesEncoder.JSON.encode(span2_1)); } @Test(expected = IllegalArgumentException.class) public void readSpans_json2_not_list() { - decoder.readSpans(Encoder.JSON.encode(span2_1)); + decoder.readSpans(BytesEncoder.JSON.encode(span2_1)); } @Test public void readSpans_json2() { - byte[] message = MessageEncoder.JSON_BYTES.encode( - Stream.of(span2_1, span2_2).map(Encoder.JSON::encode).collect(Collectors.toList()) + byte[] message = BytesMessageEncoder.JSON_TO_BYTES.encode( + Stream.of(span2_1, span2_2).map(BytesEncoder.JSON::encode).collect(Collectors.toList()) ); assertThat(decoder.readSpans(message)) diff --git a/zipkin/src/test/java/zipkin/internal/V2JsonSpanDecoderTest.java b/zipkin/src/test/java/zipkin/internal/V2JsonSpanDecoderTest.java index af7114fcfc5..2962fe0295f 100644 --- a/zipkin/src/test/java/zipkin/internal/V2JsonSpanDecoderTest.java +++ b/zipkin/src/test/java/zipkin/internal/V2JsonSpanDecoderTest.java @@ -16,8 +16,8 @@ import org.junit.Test; import zipkin.SpanDecoder; import zipkin.internal.v2.Span; -import zipkin.internal.v2.codec.Encoder; -import zipkin.internal.v2.codec.MessageEncoder; +import zipkin.internal.v2.codec.BytesEncoder; +import zipkin.internal.v2.codec.BytesMessageEncoder; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; @@ -32,13 +32,13 @@ public class V2JsonSpanDecoderTest { SpanDecoder decoder = new V2JsonSpanDecoder(); @Test(expected = UnsupportedOperationException.class) public void readSpan() { - decoder.readSpan(Encoder.JSON.encode(span2_1)); + decoder.readSpan(BytesEncoder.JSON.encode(span2_1)); } @Test public void readSpans() { - byte[] message = MessageEncoder.JSON_BYTES.encode(asList( - Encoder.JSON.encode(span2_1), - Encoder.JSON.encode(span2_2) + byte[] message = BytesMessageEncoder.JSON_TO_BYTES.encode(asList( + BytesEncoder.JSON.encode(span2_1), + BytesEncoder.JSON.encode(span2_2) )); assertThat(decoder.readSpans(message)) diff --git a/zipkin/src/test/java/zipkin/internal/V2SpanConverterTest.java b/zipkin/src/test/java/zipkin/internal/V2SpanConverterTest.java index bcdcce21905..7fab665eb78 100644 --- a/zipkin/src/test/java/zipkin/internal/V2SpanConverterTest.java +++ b/zipkin/src/test/java/zipkin/internal/V2SpanConverterTest.java @@ -26,6 +26,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static zipkin.Constants.LOCAL_COMPONENT; +import static zipkin.internal.V2SpanConverter.convert; public class V2SpanConverterTest { Endpoint frontend = Endpoint.create("frontend", 127 << 24 | 1); @@ -37,14 +38,14 @@ public class V2SpanConverterTest { Endpoint kafka = Endpoint.create("kafka", 0); @Test public void client() { - Span simpleClient = Span.builder() + Span simpleClient = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .parentId("6b221d5bc9e6496c") .id("5b4185666d50f68b") .name("get") .kind(Kind.CLIENT) - .localEndpoint(frontend) - .remoteEndpoint(backend) + .localEndpoint(convert(frontend)) + .remoteEndpoint(convert(backend)) .timestamp(1472470996199000L) .duration(207000L) .addAnnotation(1472470996238000L, Constants.WIRE_SEND) @@ -77,13 +78,13 @@ public class V2SpanConverterTest { } @Test public void client_unfinished() { - Span simpleClient = Span.builder() + Span simpleClient = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .parentId("6b221d5bc9e6496c") .id("5b4185666d50f68b") .name("get") .kind(Kind.CLIENT) - .localEndpoint(frontend) + .localEndpoint(convert(frontend)) .timestamp(1472470996199000L) .addAnnotation(1472470996238000L, Constants.WIRE_SEND) .build(); @@ -106,12 +107,12 @@ public class V2SpanConverterTest { } @Test public void client_kindInferredFromAnnotation() { - Span simpleClient = Span.builder() + Span simpleClient = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .parentId("6b221d5bc9e6496c") .id("5b4185666d50f68b") .name("get") - .localEndpoint(frontend) + .localEndpoint(convert(frontend)) .timestamp(1472470996199000L) .duration(1472470996238000L - 1472470996199000L) .addAnnotation(1472470996199000L, Constants.CLIENT_SEND) @@ -134,13 +135,13 @@ public class V2SpanConverterTest { } @Test public void noAnnotationsExceptAddresses() { - Span span2 = Span.builder() + Span span2 = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .parentId("6b221d5bc9e6496c") .id("5b4185666d50f68b") .name("get") - .localEndpoint(frontend) - .remoteEndpoint(backend) + .localEndpoint(convert(frontend)) + .remoteEndpoint(convert(backend)) .timestamp(1472470996199000L) .duration(207000L) .build(); @@ -164,13 +165,13 @@ public class V2SpanConverterTest { } @Test public void fromSpan_redundantAddressAnnotations() { - Span span2 = Span.builder() + Span span2 = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .parentId("6b221d5bc9e6496c") .id("5b4185666d50f68b") .kind(Kind.CLIENT) .name("get") - .localEndpoint(frontend) + .localEndpoint(convert(frontend)) .timestamp(1472470996199000L) .duration(207000L) .build(); @@ -194,13 +195,13 @@ public class V2SpanConverterTest { } @Test public void server() { - Span simpleServer = Span.builder() + Span simpleServer = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .id("216a2aea45d08fc9") .name("get") .kind(Kind.SERVER) - .localEndpoint(backend) - .remoteEndpoint(frontend) + .localEndpoint(convert(backend)) + .remoteEndpoint(convert(frontend)) .timestamp(1472470996199000L) .duration(207000L) .putTag(TraceKeys.HTTP_PATH, "/api") @@ -229,7 +230,7 @@ public class V2SpanConverterTest { /** Buggy instrumentation can send data with missing endpoints. Make sure we can record it. */ @Test public void missingEndpoints() { - Span span2 = Span.builder() + Span span2 = Span.newBuilder() .traceId("1") .parentId("1") .id("2") @@ -254,7 +255,7 @@ public class V2SpanConverterTest { /** No special treatment for invalid core annotations: missing endpoint */ @Test public void missingEndpoints_coreAnnotation() { - Span span2 = Span.builder() + Span span2 = Span.newBuilder() .traceId("1") .parentId("1") .id("2") @@ -279,12 +280,12 @@ public class V2SpanConverterTest { } @Test public void localSpan_emptyComponent() { - Span simpleLocal = Span.builder() + Span simpleLocal = Span.newBuilder() .traceId("1") .parentId("1") .id("2") .name("local") - .localEndpoint(frontend) + .localEndpoint(convert(frontend)) .timestamp(1472470996199000L) .duration(207000L) .build(); @@ -326,7 +327,7 @@ public class V2SpanConverterTest { .addBinaryAnnotation(BinaryAnnotation.address(Constants.SERVER_ADDR, backend)) .build(); - Span.Builder builder = Span.builder() + Span.Builder builder = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .parentId("6b221d5bc9e6496c") .id("5b4185666d50f68b") @@ -335,8 +336,8 @@ public class V2SpanConverterTest { // the client side owns timestamp and duration Span client = builder.clone() .kind(Kind.CLIENT) - .localEndpoint(frontend) - .remoteEndpoint(backend) + .localEndpoint(convert(frontend)) + .remoteEndpoint(convert(backend)) .timestamp(1472470996199000L) .duration(207000L) .addAnnotation(1472470996238000L, Constants.WIRE_SEND) @@ -349,8 +350,8 @@ public class V2SpanConverterTest { Span server = builder.clone() .kind(Kind.SERVER) .shared(true) - .localEndpoint(backend) - .remoteEndpoint(frontend) + .localEndpoint(convert(backend)) + .remoteEndpoint(convert(frontend)) .timestamp(1472470996250000L) .duration(100000L) .putTag(TraceKeys.HTTP_PATH, "/backend") @@ -376,14 +377,14 @@ public class V2SpanConverterTest { .addAnnotation(Annotation.create(1472470996350000L, Constants.SERVER_SEND, backend)) .build(); - Span span2 = Span.builder() + Span span2 = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .parentId("6b221d5bc9e6496c") .id("5b4185666d50f68b") .name("get") .kind(Kind.SERVER) .shared(true) - .localEndpoint(backend) + .localEndpoint(convert(backend)) .timestamp(1472470996250000L) .duration(100000L) .build(); @@ -407,7 +408,7 @@ public class V2SpanConverterTest { .addAnnotation(Annotation.create(1472470996406000L, Constants.CLIENT_RECV, frontend)) .build(); - Span.Builder builder = Span.builder() + Span.Builder builder = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .parentId("6b221d5bc9e6496c") .id("5b4185666d50f68b") @@ -415,7 +416,7 @@ public class V2SpanConverterTest { Span client = builder.clone() .kind(Kind.CLIENT) - .localEndpoint(frontend) + .localEndpoint(convert(frontend)) .timestamp(1472470996199000L) .duration(207000L) .build(); @@ -423,7 +424,7 @@ public class V2SpanConverterTest { Span server = builder.clone() .kind(Kind.SERVER) .shared(true) - .localEndpoint(frontend) + .localEndpoint(convert(frontend)) .timestamp(1472470996250000L) .duration(100000L) .build(); @@ -443,7 +444,7 @@ public class V2SpanConverterTest { .addAnnotation(Annotation.create(1472470996250000L, Constants.SERVER_RECV, frontend)) .build(); - Span.Builder builder = Span.builder() + Span.Builder builder = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .parentId("6b221d5bc9e6496c") .id("5b4185666d50f68b") @@ -451,14 +452,14 @@ public class V2SpanConverterTest { Span client = builder.clone() .kind(Kind.CLIENT) - .localEndpoint(frontend) + .localEndpoint(convert(frontend)) .timestamp(1472470996199000L) .build(); Span server = builder.clone() .kind(Kind.SERVER) .shared(true) - .localEndpoint(frontend) + .localEndpoint(convert(frontend)) .timestamp(1472470996250000L) .build(); @@ -476,13 +477,13 @@ public class V2SpanConverterTest { .addAnnotation(Annotation.create(1472470996199000L, Constants.MESSAGE_SEND, frontend)) .build(); - Span span2 = Span.builder() + Span span2 = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .parentId("6b221d5bc9e6496c") .id("5b4185666d50f68b") .name("send") .kind(Kind.PRODUCER) - .localEndpoint(frontend) + .localEndpoint(convert(frontend)) .timestamp(1472470996199000L) .build(); @@ -502,15 +503,15 @@ public class V2SpanConverterTest { .addBinaryAnnotation(BinaryAnnotation.address(Constants.MESSAGE_ADDR, kafka)) .build(); - Span span2 = Span.builder() + Span span2 = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .parentId("6b221d5bc9e6496c") .id("5b4185666d50f68b") .name("send") .kind(Kind.PRODUCER) - .localEndpoint(frontend) + .localEndpoint(convert(frontend)) .timestamp(1472470996199000L) - .remoteEndpoint(kafka) + .remoteEndpoint(convert(kafka)) .build(); assertThat(V2SpanConverter.toSpan(span2)) @@ -532,13 +533,13 @@ public class V2SpanConverterTest { .addAnnotation(Annotation.create(1472470996250000L, Constants.WIRE_SEND, frontend)) .build(); - Span span2 = Span.builder() + Span span2 = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .parentId("6b221d5bc9e6496c") .id("5b4185666d50f68b") .name("send") .kind(Kind.PRODUCER) - .localEndpoint(frontend) + .localEndpoint(convert(frontend)) .timestamp(1472470996199000L) .duration(51000L) .build(); @@ -560,13 +561,13 @@ public class V2SpanConverterTest { .addAnnotation(Annotation.create(1472470996199000L, Constants.MESSAGE_RECV, frontend)) .build(); - Span span2 = Span.builder() + Span span2 = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .parentId("6b221d5bc9e6496c") .id("5b4185666d50f68b") .name("send") .kind(Kind.CONSUMER) - .localEndpoint(frontend) + .localEndpoint(convert(frontend)) .timestamp(1472470996199000L) .build(); @@ -588,14 +589,14 @@ public class V2SpanConverterTest { .addBinaryAnnotation(BinaryAnnotation.address(Constants.MESSAGE_ADDR, kafka)) .build(); - Span span2 = Span.builder() + Span span2 = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .parentId("6b221d5bc9e6496c") .id("5b4185666d50f68b") .name("send") .kind(Kind.CONSUMER) - .localEndpoint(frontend) - .remoteEndpoint(kafka) + .localEndpoint(convert(frontend)) + .remoteEndpoint(convert(kafka)) .timestamp(1472470996199000L) .build(); @@ -618,13 +619,13 @@ public class V2SpanConverterTest { .addAnnotation(Annotation.create(1472470996250000L, Constants.MESSAGE_RECV, frontend)) .build(); - Span span2 = Span.builder() + Span span2 = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .parentId("6b221d5bc9e6496c") .id("5b4185666d50f68b") .name("send") .kind(Kind.CONSUMER) - .localEndpoint(frontend) + .localEndpoint(convert(frontend)) .timestamp(1472470996199000L) .duration(51000L) .build(); @@ -650,7 +651,7 @@ public class V2SpanConverterTest { .addBinaryAnnotation(BinaryAnnotation.address(Constants.MESSAGE_ADDR, kafka)) .build(); - Span.Builder builder = Span.builder() + Span.Builder builder = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .parentId("6b221d5bc9e6496c") .id("5b4185666d50f68b") @@ -658,8 +659,8 @@ public class V2SpanConverterTest { Span producer = builder.clone() .kind(Kind.PRODUCER) - .localEndpoint(frontend) - .remoteEndpoint(kafka) + .localEndpoint(convert(frontend)) + .remoteEndpoint(convert(kafka)) .timestamp(1472470996199000L) .duration(1472470996238000L - 1472470996199000L) .build(); @@ -667,8 +668,8 @@ public class V2SpanConverterTest { Span consumer = builder.clone() .kind(Kind.CONSUMER) .shared(true) - .localEndpoint(backend) - .remoteEndpoint(kafka) + .localEndpoint(convert(backend)) + .remoteEndpoint(convert(kafka)) .timestamp(1472470996403000L) .duration(1472470996406000L - 1472470996403000L) .build(); @@ -691,7 +692,7 @@ public class V2SpanConverterTest { .addAnnotation(Annotation.create(1472470996406000L, Constants.MESSAGE_RECV, frontend)) .build(); - Span.Builder builder = Span.builder() + Span.Builder builder = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .parentId("6b221d5bc9e6496c") .id("5b4185666d50f68b") @@ -699,7 +700,7 @@ public class V2SpanConverterTest { Span producer = builder.clone() .kind(Kind.PRODUCER) - .localEndpoint(frontend) + .localEndpoint(convert(frontend)) .timestamp(1472470996199000L) .duration(1472470996238000L - 1472470996199000L) .build(); @@ -707,7 +708,7 @@ public class V2SpanConverterTest { Span consumer = builder.clone() .kind(Kind.CONSUMER) .shared(true) - .localEndpoint(frontend) + .localEndpoint(convert(frontend)) .timestamp(1472470996403000L) .duration(1472470996406000L - 1472470996403000L) .build(); @@ -731,13 +732,13 @@ public class V2SpanConverterTest { .addBinaryAnnotation(BinaryAnnotation.create("missing", "", null)) .build(); - Span.Builder builder = Span.builder() + Span.Builder builder = Span.newBuilder() .traceId("216a2aea45d08fc9") .id("5b4185666d50f68b") .name("missing"); Span first = builder.clone() - .localEndpoint(frontend) + .localEndpoint(convert(frontend)) .addAnnotation(1472470996199000L, "foo") .addAnnotation(1472470996238000L, "bar") .addAnnotation(1472470996403000L, "missing") @@ -746,7 +747,7 @@ public class V2SpanConverterTest { .build(); Span second = builder.clone() - .localEndpoint(backend) + .localEndpoint(convert(backend)) .addAnnotation(1472470996250000L, "baz") .addAnnotation(1472470996350000L, "qux") .putTag("baz", "qux") @@ -776,11 +777,11 @@ public class V2SpanConverterTest { .addBinaryAnnotation(BinaryAnnotation.create("bytes", bytesBuffer, Type.BYTES, frontend)) .build(); - Span span2 = Span.builder() + Span span2 = Span.newBuilder() .traceId("1") .name("test") .id("2") - .localEndpoint(frontend) + .localEndpoint(convert(frontend)) .putTag("bool", "true") .putTag("short", "20") .putTag("int", "32800") diff --git a/zipkin/src/test/java/zipkin/internal/V2SpanStoreAdapterTest.java b/zipkin/src/test/java/zipkin/internal/V2SpanStoreAdapterTest.java index 5a6541d164b..9d9dc1d8fa3 100644 --- a/zipkin/src/test/java/zipkin/internal/V2SpanStoreAdapterTest.java +++ b/zipkin/src/test/java/zipkin/internal/V2SpanStoreAdapterTest.java @@ -41,6 +41,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static zipkin.TestObjects.TODAY; +import static zipkin.internal.V2SpanConverter.convert; public class V2SpanStoreAdapterTest { @Rule public MockitoRule mocks = MockitoJUnit.rule(); @@ -52,7 +53,7 @@ public class V2SpanStoreAdapterTest { Endpoint frontend = Endpoint.create("frontend", 192 << 24 | 168 << 16 | 2); Endpoint backend = Endpoint.create("backend", 192 << 24 | 168 << 16 | 3); - Span.Builder builder = Span.builder() + Span.Builder builder = Span.newBuilder() .traceId("7180c278b62e8f6a5b4185666d50f68b") .id("5b4185666d50f68b") .name("get"); @@ -60,14 +61,14 @@ public class V2SpanStoreAdapterTest { List skewedTrace2 = asList( builder.clone() .kind(Span.Kind.CLIENT) - .localEndpoint(frontend) + .localEndpoint(convert(frontend)) .timestamp((TODAY + 200) * 1000) .duration(120_000L) .build(), builder.clone() .kind(Span.Kind.SERVER) .shared(true) - .localEndpoint(backend) + .localEndpoint(convert(backend)) .timestamp((TODAY + 100) * 1000) // received before sent! .duration(60_000L) .build() @@ -254,6 +255,16 @@ public void getRawTrace_sync_wrapsIOE() throws IOException { verify(call).execute(); } + @Test public void getServiceNames_sortsList() throws IOException { + when(spanStore.getServiceNames()) + .thenReturn(call); + when(call.execute()) + .thenReturn(asList("foo", "bar")); + + assertThat(adapter.getServiceNames()) + .containsExactly("bar", "foo"); + } + @Test(expected = UncheckedIOException.class) public void getServiceNames_sync_wrapsIOE() throws IOException { when(spanStore.getServiceNames()) @@ -297,6 +308,16 @@ public void getServiceNames_sync_wrapsIOE() throws IOException { verify(call).execute(); } + @Test public void getSpanNames_sortsList() throws IOException { + when(spanStore.getSpanNames("service1")) + .thenReturn(call); + when(call.execute()) + .thenReturn(asList("foo", "bar")); + + assertThat(adapter.getSpanNames("service1")) + .containsExactly("bar", "foo"); + } + @Test(expected = UncheckedIOException.class) public void getSpanNames_sync_wrapsIOE() throws IOException { when(spanStore.getSpanNames("service1")) diff --git a/zipkin/src/test/java/zipkin/internal/v2/AnnotationTest.java b/zipkin/src/test/java/zipkin/internal/v2/AnnotationTest.java new file mode 100644 index 00000000000..27ee22176fb --- /dev/null +++ b/zipkin/src/test/java/zipkin/internal/v2/AnnotationTest.java @@ -0,0 +1,36 @@ +/** + * Copyright 2015-2017 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.v2; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class AnnotationTest { + @Rule public ExpectedException thrown = ExpectedException.none(); + + @Test public void messageWhenMissingValue() { + thrown.expect(NullPointerException.class); + thrown.expectMessage("value"); + + Annotation.create(1L, null); + } + + @Test public void toString_isNice() { + assertThat(Annotation.create(1L, "foo")) + .hasToString("Annotation{timestamp=1, value=foo}"); + } +} diff --git a/zipkin/src/test/java/zipkin/internal/v2/EndpointTest.java b/zipkin/src/test/java/zipkin/internal/v2/EndpointTest.java new file mode 100644 index 00000000000..c6dc098ac49 --- /dev/null +++ b/zipkin/src/test/java/zipkin/internal/v2/EndpointTest.java @@ -0,0 +1,195 @@ +/** + * Copyright 2015-2017 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.v2; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.UnknownHostException; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class EndpointTest { + @Rule public ExpectedException thrown = ExpectedException.none(); + + @Test public void missingIpv4IsNull() { + assertThat(Endpoint.newBuilder().build().ipv4()) + .isNull(); + } + + @Test public void newBuilderWithPort_0CoercesToNull() { + assertThat(Endpoint.newBuilder().port(0).build().port()) + .isNull(); + } + + @Test public void newBuilderWithPort_highest() { + assertThat(Endpoint.newBuilder().port(65535).build().port()) + .isEqualTo(65535); + } + + @Test public void ip_addr_ipv4() throws UnknownHostException { + Endpoint.Builder newBuilder = Endpoint.newBuilder(); + assertThat(newBuilder.parseIp(Inet4Address.getByName("1.2.3.4"))).isTrue(); + Endpoint endpoint = newBuilder.build(); + + assertThat(endpoint.ipv4()) + .isEqualTo("1.2.3.4"); + assertThat(endpoint.ipv6()) + .isNull(); + } + + @Test + public void ip_string_ipv4() throws UnknownHostException { + Endpoint.Builder newBuilder = Endpoint.newBuilder(); + assertThat(newBuilder.parseIp("1.2.3.4")).isTrue(); + Endpoint endpoint = newBuilder.build(); + + assertThat(endpoint.ipv4()) + .isEqualTo("1.2.3.4"); + assertThat(endpoint.ipv6()) + .isNull(); + } + + @Test public void ip_ipv6() throws UnknownHostException { + String ipv6 = "2001:db8::c001"; + Endpoint endpoint = Endpoint.newBuilder().ip(ipv6).build(); + + assertThat(endpoint.ipv4()) + .isNull(); + assertThat(endpoint.ipv6()) + .isEqualTo(ipv6); + } + + @Test public void ip_ipv6_addr() throws UnknownHostException { + String ipv6 = "2001:db8::c001"; + Endpoint endpoint = Endpoint.newBuilder().ip(Inet6Address.getByName(ipv6)).build(); + + assertThat(endpoint.ipv4()) + .isNull(); + assertThat(endpoint.ipv6()) + .isEqualTo(ipv6); + } + + @Test public void ip_ipv6_mappedIpv4() throws UnknownHostException { + String ipv6 = "::FFFF:1.2.3.4"; + Endpoint endpoint = Endpoint.newBuilder().ip(ipv6).build(); + + assertThat(endpoint.ipv4()) + .isEqualTo("1.2.3.4"); + assertThat(endpoint.ipv6()) + .isNull(); + } + + @Test public void ip_ipv6_addr_mappedIpv4() throws UnknownHostException { + String ipv6 = "::FFFF:1.2.3.4"; + Endpoint endpoint = Endpoint.newBuilder().ip(Inet6Address.getByName(ipv6)).build(); + + assertThat(endpoint.ipv4()) + .isEqualTo("1.2.3.4"); + assertThat(endpoint.ipv6()) + .isNull(); + } + + @Test public void ip_ipv6_compatIpv4() throws UnknownHostException { + String ipv6 = "::0000:1.2.3.4"; + Endpoint endpoint = Endpoint.newBuilder().ip(ipv6).build(); + + assertThat(endpoint.ipv4()) + .isEqualTo("1.2.3.4"); + assertThat(endpoint.ipv6()) + .isNull(); + } + + @Test public void ip_ipv6_addr_compatIpv4() throws UnknownHostException { + String ipv6 = "::0000:1.2.3.4"; + Endpoint endpoint = Endpoint.newBuilder().ip(Inet6Address.getByName(ipv6)).build(); + + assertThat(endpoint.ipv4()) + .isEqualTo("1.2.3.4"); + assertThat(endpoint.ipv6()) + .isNull(); + } + + @Test public void ipv6_notMappedIpv4() throws UnknownHostException { + String ipv6 = "::ffef:1.2.3.4"; + Endpoint endpoint = Endpoint.newBuilder().ip(ipv6).build(); + + assertThat(endpoint.ipv4()) + .isNull(); + assertThat(endpoint.ipv6()) + .isNull(); + } + + @Test public void ipv6_downcases() throws UnknownHostException { + Endpoint endpoint = Endpoint.newBuilder().ip("2001:DB8::C001").build(); + + assertThat(endpoint.ipv6()) + .isEqualTo("2001:db8::c001"); + } + + @Test public void ip_ipv6_compatIpv4_compressed() throws UnknownHostException { + String ipv6 = "::1.2.3.4"; + Endpoint endpoint = Endpoint.newBuilder().ip(ipv6).build(); + + assertThat(endpoint.ipv4()) + .isEqualTo("1.2.3.4"); + assertThat(endpoint.ipv6()) + .isNull(); + } + + /** This ensures we don't mistake IPv6 localhost for a mapped IPv4 0.0.0.1 */ + @Test public void ipv6_localhost() throws UnknownHostException { + String ipv6 = "::1"; + Endpoint endpoint = Endpoint.newBuilder().ip(ipv6).build(); + + assertThat(endpoint.ipv4()) + .isNull(); + assertThat(endpoint.ipv6()) + .isEqualTo(ipv6); + } + + /** This is an unusable compat Ipv4 of 0.0.0.2. This makes sure it isn't mistaken for localhost */ + @Test public void ipv6_notLocalhost() throws UnknownHostException { + String ipv6 = "::2"; + Endpoint endpoint = Endpoint.newBuilder().ip(ipv6).build(); + + assertThat(endpoint.ipv4()) + .isNull(); + assertThat(endpoint.ipv6()) + .isEqualTo(ipv6); + } + + /** The integer arg of port should be a whole number */ + @Test public void newBuilderWithPort_negativeIsInvalid() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("invalid port -1"); + + assertThat(Endpoint.newBuilder().port(-1).build().port()).isNull(); + } + + /** The integer arg of port should fit in a 16bit unsigned value */ + @Test public void newBuilderWithPort_tooHighIsInvalid() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("invalid port 65536"); + + Endpoint.newBuilder().port(65536).build(); + } + + @Test public void lowercasesServiceName() { + assertThat(Endpoint.newBuilder().serviceName("fFf").ipv4("127.0.0.1").build().serviceName()) + .isEqualTo("fff"); + } +} diff --git a/zipkin/src/test/java/zipkin/internal/v2/SpanTest.java b/zipkin/src/test/java/zipkin/internal/v2/SpanTest.java index ac86b766db7..01661ce4109 100644 --- a/zipkin/src/test/java/zipkin/internal/v2/SpanTest.java +++ b/zipkin/src/test/java/zipkin/internal/v2/SpanTest.java @@ -17,15 +17,14 @@ import java.io.ObjectOutputStream; import okio.Buffer; import org.junit.Test; -import zipkin.Annotation; -import zipkin.internal.Util; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.data.MapEntry.entry; import static zipkin.TestObjects.APP_ENDPOINT; +import static zipkin.internal.V2SpanConverter.convert; public class SpanTest { - Span base = Span.builder().traceId("1").id("1").localEndpoint(APP_ENDPOINT).build(); + Span base = Span.newBuilder().traceId("1").id("1").localEndpoint(convert(APP_ENDPOINT)).build(); @Test public void traceIdString() { Span with128BitId = base.toBuilder() @@ -49,8 +48,8 @@ public class SpanTest { // note: annotations don't also have endpoints, as it is implicit to Span.localEndpoint assertThat(span.annotations()).containsExactly( - Annotation.create(1L, "foo", null), - Annotation.create(2L, "foo", null) + Annotation.create(1L, "foo"), + Annotation.create(2L, "foo") ); } diff --git a/zipkin/src/test/java/zipkin/internal/v2/codec/MessageEncodingTest.java b/zipkin/src/test/java/zipkin/internal/v2/codec/BytesMessageEncoderTest.java similarity index 85% rename from zipkin/src/test/java/zipkin/internal/v2/codec/MessageEncodingTest.java rename to zipkin/src/test/java/zipkin/internal/v2/codec/BytesMessageEncoderTest.java index 151c6521045..c4e353beadd 100644 --- a/zipkin/src/test/java/zipkin/internal/v2/codec/MessageEncodingTest.java +++ b/zipkin/src/test/java/zipkin/internal/v2/codec/BytesMessageEncoderTest.java @@ -21,23 +21,23 @@ import static org.assertj.core.api.Assertions.assertThat; -public class MessageEncodingTest { +public class BytesMessageEncoderTest { @Test public void emptyList_jsonBytes() throws IOException { List encoded = Arrays.asList(); - assertThat(MessageEncoder.JSON_BYTES.encode(encoded)) + assertThat(BytesMessageEncoder.JSON_TO_BYTES.encode(encoded)) .isEqualTo("[]".getBytes(Util.UTF_8)); } @Test public void singletonList_jsonBytes() throws IOException { List encoded = Arrays.asList(new byte[] {'1'}); - assertThat(MessageEncoder.JSON_BYTES.encode(encoded)) + assertThat(BytesMessageEncoder.JSON_TO_BYTES.encode(encoded)) .isEqualTo("[1]".getBytes(Util.UTF_8)); } @Test public void multiItemList_jsonBytes() throws IOException { List encoded = Arrays.asList(new byte[] {'3'}, new byte[] {'4'}, new byte[] {'5'}); - assertThat(MessageEncoder.JSON_BYTES.encode(encoded)) + assertThat(BytesMessageEncoder.JSON_TO_BYTES.encode(encoded)) .isEqualTo("[3,4,5]".getBytes(Util.UTF_8)); } } diff --git a/zipkin/src/test/java/zipkin/internal/v2/codec/SpanJsonAdaptersTest.java b/zipkin/src/test/java/zipkin/internal/v2/codec/SpanJsonAdaptersTest.java index f9c626c0f2d..e9196843acd 100644 --- a/zipkin/src/test/java/zipkin/internal/v2/codec/SpanJsonAdaptersTest.java +++ b/zipkin/src/test/java/zipkin/internal/v2/codec/SpanJsonAdaptersTest.java @@ -29,6 +29,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static zipkin.internal.Util.UTF_8; +import static zipkin.internal.V2SpanConverter.convert; public class SpanJsonAdaptersTest { Endpoint frontend = Endpoint.create("frontend", 127 << 24 | 1); @@ -38,14 +39,14 @@ public class SpanJsonAdaptersTest { .port(9000) .build(); - Span span = Span.builder() + Span span = Span.newBuilder() .traceId("7180c278b62e8f6a216a2aea45d08fc9") .parentId("6b221d5bc9e6496c") .id("5b4185666d50f68b") .name("get") .kind(Span.Kind.CLIENT) - .localEndpoint(frontend) - .remoteEndpoint(backend) + .localEndpoint(convert(frontend)) + .remoteEndpoint(convert(backend)) .timestamp(1472470996199000L) .duration(207000L) .addAnnotation(1472470996238000L, Constants.WIRE_SEND) @@ -57,26 +58,26 @@ public class SpanJsonAdaptersTest { @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void spanRoundTrip() throws IOException { - assertThat(Decoder.JSON.decodeList(encodeList(span))) + assertThat(BytesDecoder.JSON.decodeList(encodeList(span))) .containsOnly(span); } @Test public void sizeInBytes() throws IOException { assertThat(Span2JsonAdapters.SPAN_WRITER.sizeInBytes(span)) - .isEqualTo(Encoder.JSON.encode(span).length); + .isEqualTo(BytesEncoder.JSON.encode(span).length); } @Test public void spanRoundTrip_64bitTraceId() throws IOException { span = span.toBuilder().traceId(span.traceId().substring(16)).build(); - assertThat(Decoder.JSON.decodeList(encodeList(span))) + assertThat(BytesDecoder.JSON.decodeList(encodeList(span))) .containsOnly(span); } @Test public void spanRoundTrip_shared() throws IOException { span = span.toBuilder().shared(true).build(); - assertThat(Decoder.JSON.decodeList(encodeList(span))) + assertThat(BytesDecoder.JSON.decodeList(encodeList(span))) .containsOnly(span); } @@ -84,7 +85,7 @@ public class SpanJsonAdaptersTest { span = span.toBuilder().traceId(span.traceId().substring(16)).build(); assertThat(Span2JsonAdapters.SPAN_WRITER.sizeInBytes(span)) - .isEqualTo(Encoder.JSON.encode(span).length); + .isEqualTo(BytesEncoder.JSON.encode(span).length); } /** @@ -93,17 +94,16 @@ public class SpanJsonAdaptersTest { */ @Test public void specialCharsInJson() throws IOException { // service name is surrounded by control characters - Span worstSpanInTheWorld = Span.builder().traceId("1").id("1") + Span worstSpanInTheWorld = Span.newBuilder().traceId("1").id("1") // name is terrible .name(new String(new char[] {'"', '\\', '\t', '\b', '\n', '\r', '\f'})) - .localEndpoint(Endpoint.create(new String(new char[] {0, 'a', 1}), 0)) // annotation value includes some json newline characters .addAnnotation(1L, "\u2028 and \u2029") // tag key includes a quote and value newlines .putTag("\"foo", "Database error: ORA-00942:\u2028 and \u2029 table or view does not exist\n") .build(); - assertThat(Decoder.JSON.decodeList(encodeList(worstSpanInTheWorld))) + assertThat(BytesDecoder.JSON.decodeList(encodeList(worstSpanInTheWorld))) .containsOnly(worstSpanInTheWorld); } @@ -117,14 +117,14 @@ public class SpanJsonAdaptersTest { + " \"id\": \"6b221d5bc9e6496c\"\n" + "}]"; - Decoder.JSON.decodeList(json.getBytes(UTF_8)); + BytesDecoder.JSON.decodeList(json.getBytes(UTF_8)); } @Test public void niceErrorOnEmpty_inputSpans() throws IOException { thrown.expect(IllegalArgumentException.class); thrown.expectMessage("Empty input reading List"); - Decoder.JSON.decodeList(new byte[0]); + BytesDecoder.JSON.decodeList(new byte[0]); } /** @@ -134,27 +134,27 @@ public class SpanJsonAdaptersTest { thrown.expect(IllegalArgumentException.class); thrown.expectMessage("Malformed reading List from "); - Decoder.JSON.decodeList(new byte[] {'h', 'e', 'l', 'l', 'o'}); + BytesDecoder.JSON.decodeList(new byte[] {'h', 'e', 'l', 'l', 'o'}); } @Test public void spansRoundTrip() throws IOException { List tenClientSpans = Collections.nCopies(10, span); - byte[] message = MessageEncoder.JSON_BYTES.encode( - tenClientSpans.stream().map(Encoder.JSON::encode).collect(Collectors.toList()) + byte[] message = BytesMessageEncoder.JSON_TO_BYTES.encode( + tenClientSpans.stream().map(BytesEncoder.JSON::encode).collect(Collectors.toList()) ); - assertThat(Decoder.JSON.decodeList(message)) + assertThat(BytesDecoder.JSON.decodeList(message)) .isEqualTo(tenClientSpans); } @Test public void writesTraceIdHighIntoTraceIdField() { - Span with128BitTraceId = Span.builder() + Span with128BitTraceId = Span.newBuilder() .traceId("48485a3953bb61246b221d5bc9e6496c") - .localEndpoint(frontend) + .localEndpoint(convert(frontend)) .id("1").name("").build(); - assertThat(new String(Encoder.JSON.encode(with128BitTraceId), Util.UTF_8)) + assertThat(new String(BytesEncoder.JSON.encode(with128BitTraceId), Util.UTF_8)) .startsWith("{\"traceId\":\"48485a3953bb61246b221d5bc9e6496c\""); } @@ -170,8 +170,8 @@ public class SpanJsonAdaptersTest { + " \"id\": \"6b221d5bc9e6496c\"\n" + "}]").getBytes(UTF_8); - assertThat(Decoder.JSON.decodeList(with128BitTraceId).get(0)) - .isEqualTo(Decoder.JSON.decodeList(withLower64bitsTraceId).get(0).toBuilder() + assertThat(BytesDecoder.JSON.decodeList(with128BitTraceId).get(0)) + .isEqualTo(BytesDecoder.JSON.decodeList(withLower64bitsTraceId).get(0).toBuilder() .traceId("48485a3953bb61246b221d5bc9e6496c").build()); } @@ -191,7 +191,7 @@ public class SpanJsonAdaptersTest { + " \"shared\": null\n" + "}]"; - Decoder.JSON.decodeList(json.getBytes(UTF_8)); + BytesDecoder.JSON.decodeList(json.getBytes(UTF_8)); } @Test public void ignoresNull_endpoint_topLevelFields() { @@ -207,7 +207,7 @@ public class SpanJsonAdaptersTest { + " }\n" + "}]"; - assertThat(Decoder.JSON.decodeList(json.getBytes(UTF_8)).get(0).localEndpoint()) + assertThat(convert(BytesDecoder.JSON.decodeList(json.getBytes(UTF_8)).get(0).localEndpoint())) .isEqualTo(Endpoint.create("", 127 << 24 | 1)); } @@ -226,7 +226,7 @@ public class SpanJsonAdaptersTest { + " \"port\": null\n" + " }\n" + "}]"; - Decoder.JSON.decodeList(json.getBytes(UTF_8)); + BytesDecoder.JSON.decodeList(json.getBytes(UTF_8)); } @Test public void niceErrorOnIncomplete_annotation() { @@ -242,7 +242,7 @@ public class SpanJsonAdaptersTest { + " ]\n" + "}]"; - Decoder.JSON.decodeList(json.getBytes(UTF_8)); + BytesDecoder.JSON.decodeList(json.getBytes(UTF_8)); } @Test public void niceErrorOnNull_traceId() { @@ -255,7 +255,7 @@ public class SpanJsonAdaptersTest { + " \"id\": \"6b221d5bc9e6496c\"\n" + "}]"; - Decoder.JSON.decodeList(json.getBytes(UTF_8)); + BytesDecoder.JSON.decodeList(json.getBytes(UTF_8)); } @Test public void niceErrorOnNull_id() { @@ -268,7 +268,7 @@ public class SpanJsonAdaptersTest { + " \"id\": null\n" + "}]"; - Decoder.JSON.decodeList(json.getBytes(UTF_8)); + BytesDecoder.JSON.decodeList(json.getBytes(UTF_8)); } @Test public void niceErrorOnNull_tagValue() { @@ -284,7 +284,7 @@ public class SpanJsonAdaptersTest { + " }\n" + "}]"; - Decoder.JSON.decodeList(json.getBytes(UTF_8)); + BytesDecoder.JSON.decodeList(json.getBytes(UTF_8)); } @Test public void niceErrorOnNull_annotationValue() { @@ -300,7 +300,7 @@ public class SpanJsonAdaptersTest { + " ]\n" + "}]"; - Decoder.JSON.decodeList(json.getBytes(UTF_8)); + BytesDecoder.JSON.decodeList(json.getBytes(UTF_8)); } @Test public void niceErrorOnNull_annotationTimestamp() { @@ -316,7 +316,7 @@ public class SpanJsonAdaptersTest { + " ]\n" + "}]"; - Decoder.JSON.decodeList(json.getBytes(UTF_8)); + BytesDecoder.JSON.decodeList(json.getBytes(UTF_8)); } @Test public void readSpan_localEndpoint_noServiceName() { @@ -329,7 +329,7 @@ public class SpanJsonAdaptersTest { + " }\n" + "}]"; - assertThat(Decoder.JSON.decodeList(json.getBytes(UTF_8)).get(0).localEndpoint()) + assertThat(convert(BytesDecoder.JSON.decodeList(json.getBytes(UTF_8)).get(0).localEndpoint())) .isEqualTo(Endpoint.create("", 127 << 24 | 1)); } @@ -343,29 +343,30 @@ public class SpanJsonAdaptersTest { + " }\n" + "}]"; - assertThat(Decoder.JSON.decodeList(json.getBytes(UTF_8)).get(0).remoteEndpoint()) + assertThat(convert(BytesDecoder.JSON.decodeList(json.getBytes(UTF_8)).get(0).remoteEndpoint())) .isEqualTo(Endpoint.create("", 127 << 24 | 1)); } @Test public void spanRoundTrip_noRemoteServiceName() throws IOException { - span = span.toBuilder().remoteEndpoint(backend.toBuilder().serviceName("").build()).build(); + span = span.toBuilder() + .remoteEndpoint(convert(backend.toBuilder().serviceName("").build())).build(); - assertThat(Decoder.JSON.decodeList(encodeList(span))) + assertThat(BytesDecoder.JSON.decodeList(encodeList(span))) .containsOnly(span); } @Test public void doesntWriteEmptyServiceName() throws IOException { span = span.toBuilder() - .localEndpoint(frontend.toBuilder().serviceName("").build()) + .localEndpoint(convert(frontend.toBuilder().serviceName("").build())) .remoteEndpoint(null).build(); - assertThat(new String(Encoder.JSON.encode(span), UTF_8)) + assertThat(new String(BytesEncoder.JSON.encode(span), UTF_8)) .contains("{\"ipv4\":\"127.0.0.1\"}"); } static byte[] encodeList(Span... spans) { - return MessageEncoder.JSON_BYTES.encode( - Arrays.stream(spans).map(Encoder.JSON::encode).collect(Collectors.toList()) + return BytesMessageEncoder.JSON_TO_BYTES.encode( + Arrays.stream(spans).map(BytesEncoder.JSON::encode).collect(Collectors.toList()) ); } } diff --git a/zipkin/src/test/java/zipkin/internal/v2/storage/InMemoryStorageTest.java b/zipkin/src/test/java/zipkin/internal/v2/storage/InMemoryStorageTest.java index 6125eaa80c8..deb62161d0f 100644 --- a/zipkin/src/test/java/zipkin/internal/v2/storage/InMemoryStorageTest.java +++ b/zipkin/src/test/java/zipkin/internal/v2/storage/InMemoryStorageTest.java @@ -20,13 +20,12 @@ import java.util.stream.IntStream; import org.junit.Test; import zipkin.DependencyLink; -import zipkin.Endpoint; +import zipkin.internal.v2.Endpoint; import zipkin.internal.v2.Span; import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; -import static zipkin.TestObjects.APP_ENDPOINT; import static zipkin.TestObjects.DAY; import static zipkin.TestObjects.TODAY; import static zipkin.internal.Util.toLowerHex; @@ -35,17 +34,18 @@ public class InMemoryStorageTest { InMemoryStorage storage = InMemoryStorage.newBuilder().build(); @Test public void getTraces_filteringMatchesMostRecentTraces() throws IOException { - List endpoints = IntStream.rangeClosed(1, 10).mapToObj(i -> - Endpoint.create("service" + i, 127 << 24 | i)) + List endpoints = IntStream.rangeClosed(1, 10) + .mapToObj(i -> Endpoint.newBuilder().serviceName("service" + i).ip("127.0.0.1").build()) .collect(Collectors.toList()); long gapBetweenSpans = 100; - List earlySpans = IntStream.rangeClosed(1, 10).mapToObj(i -> Span.builder().name("early") - .traceId(toLowerHex(i)).id(toLowerHex(i)) - .timestamp((TODAY - i) * 1000).duration(1L) - .localEndpoint(endpoints.get(i - 1)).build()).collect(toList()); + List earlySpans = + IntStream.rangeClosed(1, 10).mapToObj(i -> Span.newBuilder().name("early") + .traceId(toLowerHex(i)).id(toLowerHex(i)) + .timestamp((TODAY - i) * 1000).duration(1L) + .localEndpoint(endpoints.get(i - 1)).build()).collect(toList()); - List lateSpans = IntStream.rangeClosed(1, 10).mapToObj(i -> Span.builder().name("late") + List lateSpans = IntStream.rangeClosed(1, 10).mapToObj(i -> Span.newBuilder().name("late") .traceId(toLowerHex(i + 10)).id(toLowerHex(i + 10)) .timestamp((TODAY + gapBetweenSpans - i) * 1000).duration(1L) .localEndpoint(endpoints.get(i - 1)).build()).collect(toList()); @@ -80,10 +80,10 @@ public class InMemoryStorageTest { /** It should be safe to run dependency link jobs twice */ @Test public void replayOverwrites() throws IOException { - Span span = Span.builder().traceId("10").id("10").name("receive") + Span span = Span.newBuilder().traceId("10").id("10").name("receive") .kind(Span.Kind.CONSUMER) - .localEndpoint(APP_ENDPOINT) - .remoteEndpoint(Endpoint.builder().serviceName("kafka").build()) + .localEndpoint(Endpoint.newBuilder().serviceName("app").build()) + .remoteEndpoint(Endpoint.newBuilder().serviceName("kafka").build()) .timestamp(TODAY * 1000) .build(); diff --git a/zipkin/src/test/java/zipkin/internal/v2/storage/QueryRequestTest.java b/zipkin/src/test/java/zipkin/internal/v2/storage/QueryRequestTest.java index a33325b504f..bed7c77edb1 100644 --- a/zipkin/src/test/java/zipkin/internal/v2/storage/QueryRequestTest.java +++ b/zipkin/src/test/java/zipkin/internal/v2/storage/QueryRequestTest.java @@ -19,11 +19,11 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import zipkin.Constants; +import zipkin.internal.v2.Endpoint; import zipkin.internal.v2.Span; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; -import static zipkin.TestObjects.APP_ENDPOINT; import static zipkin.TestObjects.DAY; import static zipkin.TestObjects.TODAY; import static zipkin.TraceKeys.HTTP_METHOD; @@ -31,8 +31,8 @@ public class QueryRequestTest { @Rule public ExpectedException thrown = ExpectedException.none(); QueryRequest.Builder queryBuilder = QueryRequest.newBuilder().endTs(TODAY).lookback(60).limit(10); - Span span = Span.builder().traceId("10").id("10").name("receive") - .localEndpoint(APP_ENDPOINT) + Span span = Span.newBuilder().traceId("10").id("10").name("receive") + .localEndpoint(Endpoint.newBuilder().serviceName("app").build()) .kind(Span.Kind.CONSUMER) .timestamp(TODAY * 1000) .build();