From 5bc51b17511496532c6e215c563387934ca898dd Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 31 Aug 2017 15:19:47 +0800 Subject: [PATCH 1/2] Changes v2 trace ID to hex --- .../zipkin/benchmarks/SpanBenchmarks.java | 12 +- .../java/zipkin/junit/ZipkinDispatcher.java | 5 +- .../java/zipkin/junit/v2/HttpV2SpanStore.java | 6 +- .../java/zipkin/server/ZipkinQueryApiV2.java | 14 +- .../http/ElasticsearchHttpSpanStore.java | 40 +++-- .../http/ElasticsearchHttpStorage.java | 2 + .../ElasticsearchHttpSpanConsumerTest.java | 26 +-- .../http/ElasticsearchHttpSpanStoreTest.java | 19 ++ .../elasticsearch/http/JsonAdaptersTest.java | 5 +- .../mysql/DependencyLinkV2SpanIterator.java | 14 +- .../zipkin/internal/CorrectForClockSkew.java | 8 +- .../zipkin/internal/DependencyLinker.java | 4 +- .../src/main/java/zipkin/internal/Node.java | 18 +- .../java/zipkin/internal/V2Collector.java | 4 +- .../java/zipkin/internal/V2SpanConverter.java | 22 ++- .../zipkin/internal/V2SpanStoreAdapter.java | 5 +- .../main/java/zipkin/internal/v2/Span.java | 170 ++++++++---------- .../internal/v2/codec/Span2JsonAdapters.java | 13 +- .../internal/v2/storage/InMemoryStorage.java | 42 ++--- .../zipkin/internal/v2/storage/SpanStore.java | 9 +- .../zipkin/internal/DependencyLinkerTest.java | 10 +- .../test/java/zipkin/internal/NodeTest.java | 22 ++- .../zipkin/internal/V2SpanConverterTest.java | 22 +-- .../internal/V2SpanStoreAdapterTest.java | 20 +-- .../java/zipkin/internal/v2/SpanTest.java | 48 +---- .../v2/codec/SpanJsonAdaptersTest.java | 16 +- .../v2/storage/InMemoryStorageTest.java | 9 +- .../internal/v2/storage/QueryRequestTest.java | 16 +- 28 files changed, 288 insertions(+), 313 deletions(-) diff --git a/benchmarks/src/main/java/zipkin/benchmarks/SpanBenchmarks.java b/benchmarks/src/main/java/zipkin/benchmarks/SpanBenchmarks.java index 7cb5fd54d5b..8391af22e2f 100644 --- a/benchmarks/src/main/java/zipkin/benchmarks/SpanBenchmarks.java +++ b/benchmarks/src/main/java/zipkin/benchmarks/SpanBenchmarks.java @@ -69,8 +69,10 @@ public zipkin.Span buildLocalSpan() { .build(); } - static final long traceId = Util.lowerHexToUnsignedLong("86154a4ba6e91385"); - static final long spanId = Util.lowerHexToUnsignedLong("4d1e00c0db9010db"); + static final String traceIdHex = "86154a4ba6e91385"; + static final String spanIdHex = "4d1e00c0db9010db"; + static final long traceId = Util.lowerHexToUnsignedLong(traceIdHex); + static final long spanId = Util.lowerHexToUnsignedLong(spanIdHex); static final Endpoint frontend = Endpoint.create("frontend", 127 << 24 | 1); static final Endpoint backend = Endpoint.builder() .serviceName("backend") @@ -113,9 +115,9 @@ public Span buildClientOnlySpan2() { static Span buildClientOnlySpan2(Span.Builder builder) { return builder - .traceId(traceId) - .parentId(traceId) - .id(spanId) + .traceId(traceIdHex) + .parentId(traceIdHex) + .id(spanIdHex) .name("get") .kind(Span.Kind.CLIENT) .localEndpoint(frontend) diff --git a/zipkin-junit/src/main/java/zipkin/junit/ZipkinDispatcher.java b/zipkin-junit/src/main/java/zipkin/junit/ZipkinDispatcher.java index d4cb7d6fbfb..131c35cf0e0 100644 --- a/zipkin-junit/src/main/java/zipkin/junit/ZipkinDispatcher.java +++ b/zipkin-junit/src/main/java/zipkin/junit/ZipkinDispatcher.java @@ -39,6 +39,7 @@ import zipkin.storage.SpanStore; import static zipkin.internal.Util.lowerHexToUnsignedLong; +import static zipkin.internal.v2.Span.normalizeTraceId; final class ZipkinDispatcher extends Dispatcher { static final long DEFAULT_LOOKBACK = 86400000L; // 1 day in millis @@ -142,9 +143,7 @@ MockResponse queryV2(HttpUrl url) throws IOException { return jsonResponse(bout.toByteArray()); } else if (url.encodedPath().startsWith("/api/v2/trace/")) { String traceIdHex = url.encodedPath().replace("/api/v2/trace/", ""); - long traceIdHigh = traceIdHex.length() == 32 ? lowerHexToUnsignedLong(traceIdHex, 0) : 0L; - long traceIdLow = lowerHexToUnsignedLong(traceIdHex); - List trace = store2.getTrace(traceIdHigh, traceIdLow).execute(); + List trace = store2.getTrace(normalizeTraceId(traceIdHex)).execute(); if (!trace.isEmpty()) { ByteArrayOutputStream bout = new ByteArrayOutputStream(); writeTrace(bout, trace); 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 c78e5a40095..ce85db731a9 100644 --- a/zipkin-junit/src/test/java/zipkin/junit/v2/HttpV2SpanStore.java +++ b/zipkin-junit/src/test/java/zipkin/junit/v2/HttpV2SpanStore.java @@ -21,7 +21,6 @@ import okhttp3.Request; import zipkin.Codec; import zipkin.DependencyLink; -import zipkin.internal.Util; import zipkin.internal.v2.Call; import zipkin.internal.v2.Span; import zipkin.internal.v2.codec.Decoder; @@ -50,10 +49,9 @@ final class HttpV2SpanStore implements SpanStore { content -> Decoder.JSON.decodeNestedList(content.readByteArray())); } - @Override public Call> getTrace(long traceIdHigh, long traceIdLow) { - String traceIdHex = Util.toLowerHex(traceIdHigh, traceIdLow); + @Override public Call> getTrace(String traceId) { return factory.newCall(new Request.Builder() - .url(factory.baseUrl.resolve("/api/v2/trace/" + traceIdHex)) + .url(factory.baseUrl.resolve("/api/v2/trace/" + Span.normalizeTraceId(traceId))) .build(), content -> Decoder.JSON.decodeList(content.readByteArray())) .handleError(((error, callback) -> { if (error instanceof HttpException && ((HttpException) error).code == 404) { diff --git a/zipkin-server/src/main/java/zipkin/server/ZipkinQueryApiV2.java b/zipkin-server/src/main/java/zipkin/server/ZipkinQueryApiV2.java index 1b4e974b169..7e019eabed2 100644 --- a/zipkin-server/src/main/java/zipkin/server/ZipkinQueryApiV2.java +++ b/zipkin-server/src/main/java/zipkin/server/ZipkinQueryApiV2.java @@ -42,7 +42,6 @@ import zipkin.storage.StorageComponent; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; -import static zipkin.internal.Util.lowerHexToUnsignedLong; @RestController @RequestMapping("/api/v2") @@ -147,12 +146,8 @@ public String getTraces( public String getTrace(@PathVariable String traceIdHex, WebRequest request) throws IOException { if (storage == null) throw new Version2StorageNotConfigured(); - long traceIdHigh = traceIdHex.length() == 32 ? lowerHexToUnsignedLong(traceIdHex, 0) : 0L; - long traceIdLow = lowerHexToUnsignedLong(traceIdHex); - List trace = storage.v2SpanStore().getTrace(traceIdHigh, traceIdLow).execute(); - if (trace.isEmpty()) { - throw new TraceNotFoundException(traceIdHex, traceIdHigh, traceIdLow); - } + List trace = storage.v2SpanStore().getTrace(traceIdHex).execute(); + if (trace.isEmpty()) throw new TraceNotFoundException(traceIdHex); Buffer buffer = new Buffer(); buffer.writeByte('['); for (int i = 0, length = trace.size(); i < length; ) { @@ -181,9 +176,8 @@ public void notFound() { } static class TraceNotFoundException extends RuntimeException { - TraceNotFoundException(String traceIdHex, long traceIdHigh, long traceId) { - super(String.format("Cannot find trace for id=%s, parsed value=%s", traceIdHex, - traceIdHigh != 0 ? traceIdHigh + "," + traceId : traceId)); + TraceNotFoundException(String traceIdHex) { + super("Cannot find trace " + traceIdHex); } } diff --git a/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpSpanStore.java b/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpSpanStore.java index 86eede3174b..75850044102 100644 --- a/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpSpanStore.java +++ b/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpSpanStore.java @@ -23,8 +23,6 @@ import java.util.Locale; import java.util.Map; import zipkin.DependencyLink; -import zipkin.internal.Pair; -import zipkin.internal.Util; import zipkin.internal.v2.Call; import zipkin.internal.v2.Span; import zipkin.internal.v2.storage.QueryRequest; @@ -92,14 +90,14 @@ final class ElasticsearchHttpSpanStore implements SpanStore { // be no significant difference in user experience since span start times are usually very // close to each other in human time. Aggregation traceIdTimestamp = Aggregation.terms("traceId", request.limit()) - .addSubAggregation(Aggregation.min("timestamp_millis")) - .orderBy("timestamp_millis", "desc"); + .addSubAggregation(Aggregation.min("timestamp_millis")) + .orderBy("timestamp_millis", "desc"); List indices = indexNameFormatter.formatTypeAndRange(SPAN, beginMillis, endMillis); if (indices.isEmpty()) return Call.emptyList(); SearchRequest esRequest = SearchRequest.create(indices) - .filters(filters).addAggregation(traceIdTimestamp); + .filters(filters).addAggregation(traceIdTimestamp); HttpCall> traceIdsCall = search.newCall(esRequest, BodyConverters.SORTED_KEYS); @@ -111,7 +109,7 @@ final class ElasticsearchHttpSpanStore implements SpanStore { // Due to tokenization of the trace ID, our matches are imprecise on Span.traceIdHigh for (Iterator> trace = traces.iterator(); trace.hasNext(); ) { List next = trace.next(); - if (next.get(0).traceIdHigh() != 0 && !request.test(next)) { + if (next.get(0).traceId().length() > 16 && !request.test(next)) { trace.remove(); } } @@ -126,12 +124,14 @@ final class ElasticsearchHttpSpanStore implements SpanStore { }); } - @Override public Call> getTrace(long traceIdHigh, long traceIdLow) { - String traceIdHex = Util.toLowerHex(strictTraceId ? traceIdHigh : 0L, traceIdLow); + @Override public Call> getTrace(String traceId) { + // make sure we have a 16 or 32 character trace ID + traceId = Span.normalizeTraceId(traceId); - SearchRequest request = SearchRequest.create(asList(allSpanIndices)) - .term("traceId", traceIdHex); + // Unless we are strict, truncate the trace ID to 64bit (encoded as 16 characters) + if (!strictTraceId && traceId.length() == 32) traceId = traceId.substring(16); + SearchRequest request = SearchRequest.create(asList(allSpanIndices)).term("traceId", traceId); return search.newCall(request, BodyConverters.SPANS); } @@ -147,9 +147,9 @@ final class ElasticsearchHttpSpanStore implements SpanStore { SearchRequest.Filters filters = new SearchRequest.Filters(); filters.addRange("timestamp_millis", beginMillis, endMillis); SearchRequest request = SearchRequest.create(indices) - .filters(filters) - .addAggregation(Aggregation.terms("localEndpoint.serviceName", Integer.MAX_VALUE)) - .addAggregation(Aggregation.terms("remoteEndpoint.serviceName", Integer.MAX_VALUE)); + .filters(filters) + .addAggregation(Aggregation.terms("localEndpoint.serviceName", Integer.MAX_VALUE)) + .addAggregation(Aggregation.terms("remoteEndpoint.serviceName", Integer.MAX_VALUE)); return search.newCall(request, BodyConverters.SORTED_KEYS); } @@ -164,12 +164,12 @@ final class ElasticsearchHttpSpanStore implements SpanStore { // A span name is only valid on a local endpoint, as a span name is defined locally SearchRequest.Filters filters = new SearchRequest.Filters() - .addRange("timestamp_millis", beginMillis, endMillis) - .addTerm("localEndpoint.serviceName", serviceName.toLowerCase(Locale.ROOT)); + .addRange("timestamp_millis", beginMillis, endMillis) + .addTerm("localEndpoint.serviceName", serviceName.toLowerCase(Locale.ROOT)); SearchRequest request = SearchRequest.create(indices) - .filters(filters) - .addAggregation(Aggregation.terms("name", Integer.MAX_VALUE)); + .filters(filters) + .addAggregation(Aggregation.terms("name", Integer.MAX_VALUE)); return search.newCall(request, BodyConverters.SORTED_KEYS); } @@ -188,9 +188,11 @@ final class ElasticsearchHttpSpanStore implements SpanStore { static List> groupByTraceId(Collection input, boolean strictTraceId) { if (input.isEmpty()) return Collections.emptyList(); - Map, List> groupedByTraceId = new LinkedHashMap<>(); + Map> groupedByTraceId = new LinkedHashMap<>(); for (Span span : input) { - Pair traceId = Pair.create(strictTraceId ? span.traceIdHigh() : 0L, span.traceId()); + String traceId = strictTraceId || span.traceId().length() == 16 + ? span.traceId() + : span.traceId().substring(16); if (!groupedByTraceId.containsKey(traceId)) { groupedByTraceId.put(traceId, new LinkedList<>()); } diff --git a/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpStorage.java b/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpStorage.java index ff7c2104cb7..d782c6494c7 100644 --- a/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpStorage.java +++ b/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpStorage.java @@ -73,6 +73,8 @@ public static Builder builder() { return result; } + abstract Builder toBuilder(); + @AutoValue.Builder public static abstract class Builder implements zipkin.storage.StorageComponent.Builder { abstract Builder client(OkHttpClient client); 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 a38df3cea6d..e4cb83dcef1 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 @@ -62,7 +62,7 @@ public void close() throws IOException { @Test public void addsTimestamp_millisIntoJson() throws Exception { es.enqueue(new MockResponse()); - Span span = Span.builder().traceId(20L).id(20L).name("get") + Span span = Span.builder().traceId("20").id("20").name("get") .timestamp(TODAY * 1000).build(); accept(span); @@ -72,7 +72,7 @@ public void close() throws IOException { } @Test public void prefixWithTimestampMillisAndQuery_skipsWhenNoData() throws Exception { - Span span = Span.builder().traceId(20L).id(22L).name("").parentId(21L).timestamp(0L) + Span span = Span.builder().traceId("20").id("22").name("").parentId("21").timestamp(0L) .localEndpoint(TestObjects.WEB_ENDPOINT) .kind(Kind.CLIENT) .build(); @@ -84,7 +84,7 @@ public void close() throws IOException { } @Test public void prefixWithTimestampMillisAndQuery_addsTimestampMillis() throws Exception { - Span span = Span.builder().traceId(20L).id(22L).name("").parentId(21L).timestamp(1L) + Span span = Span.builder().traceId("20").id("22").name("").parentId("21").timestamp(1L) .localEndpoint(TestObjects.WEB_ENDPOINT) .kind(Kind.CLIENT) .build(); @@ -96,7 +96,7 @@ public void close() throws IOException { } @Test public void prefixWithTimestampMillisAndQuery_addsAnnotationQuery() throws Exception { - Span span = Span.builder().traceId(20L).id(22L).name("").parentId(21L) + Span span = Span.builder().traceId("20").id("22").name("").parentId("21") .localEndpoint(TestObjects.WEB_ENDPOINT) .addAnnotation(1L, "\"foo") .build(); @@ -108,7 +108,7 @@ public void close() throws IOException { } @Test public void prefixWithTimestampMillisAndQuery_addsAnnotationQueryTags() throws Exception { - Span span = Span.builder().traceId(20L).id(22L).name("").parentId(21L) + Span span = Span.builder().traceId("20").id("22").name("").parentId("21") .localEndpoint(TestObjects.WEB_ENDPOINT) .putTag("\"foo", "\"bar") .build(); @@ -120,7 +120,7 @@ public void close() throws IOException { } @Test public void prefixWithTimestampMillisAndQuery_readable() throws Exception { - Span span = Span.builder().traceId(20L).id(20L).name("get") + Span span = Span.builder().traceId("20").id("20").name("get") .timestamp(TODAY * 1000).build(); byte[] message = MessageEncoder.JSON_BYTES.encode(asList( @@ -134,7 +134,7 @@ public void close() throws IOException { @Test public void doesntWriteDocumentId() throws Exception { es.enqueue(new MockResponse()); - accept(Span.builder().traceId(1L).id(1L).name("foo").build()); + accept(Span.builder().traceId("1").id("1").name("foo").build()); RecordedRequest request = es.takeRequest(); assertThat(request.getBody().readByteString().utf8()) @@ -144,8 +144,8 @@ public void close() throws IOException { @Test public void writesSpanNaturallyWhenNoTimestamp() throws Exception { es.enqueue(new MockResponse()); - Span span = Span.builder().traceId(1L).id(1L).name("foo").build(); - accept(Span.builder().traceId(1L).id(1L).name("foo").build()); + Span span = Span.builder().traceId("1").id("1").name("foo").build(); + accept(Span.builder().traceId("1").id("1").name("foo").build()); assertThat(es.takeRequest().getBody().readByteString().utf8()) .contains("\n" + new String(Encoder.JSON.encode(span), UTF_8) + "\n"); @@ -154,13 +154,13 @@ public void close() throws IOException { @Test public void traceIsSearchableByServerServiceName() throws Exception { es.enqueue(new MockResponse()); - Span clientSpan = Span.builder().traceId(20L).id(22L).name("").parentId(21L) + Span clientSpan = Span.builder().traceId("20").id("22").name("").parentId("21") .timestamp(1000L) .kind(Kind.CLIENT) .localEndpoint(TestObjects.WEB_ENDPOINT) .build(); - Span serverSpan = Span.builder().traceId(20L).id(22L).name("get").parentId(21L) + Span serverSpan = Span.builder().traceId("20").id("22").name("get").parentId("21") .timestamp(2000L) .kind(Kind.SERVER) .localEndpoint(TestObjects.APP_ENDPOINT) @@ -185,7 +185,7 @@ public void close() throws IOException { es.enqueue(new MockResponse()); - accept(Span.builder().traceId(1L).id(1L).name("foo").build()); + accept(Span.builder().traceId("1").id("1").name("foo").build()); RecordedRequest request = es.takeRequest(); assertThat(request.getPath()) @@ -195,7 +195,7 @@ public void close() throws IOException { @Test public void choosesTypeSpecificIndex() throws Exception { es.enqueue(new MockResponse()); - Span span = Span.builder().traceId(1L).id(2L).parentId(1L).name("s") + Span span = Span.builder().traceId("1").id("2").parentId("1").name("s") .localEndpoint(TestObjects.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/ElasticsearchHttpSpanStoreTest.java b/zipkin-storage/elasticsearch-http/src/test/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpSpanStoreTest.java index 2b7bbf2f2b2..38548bf840e 100644 --- a/zipkin-storage/elasticsearch-http/src/test/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpSpanStoreTest.java +++ b/zipkin-storage/elasticsearch-http/src/test/java/zipkin/storage/elasticsearch/http/ElasticsearchHttpSpanStoreTest.java @@ -41,6 +41,25 @@ public class ElasticsearchHttpSpanStoreTest { storage.close(); } + @Test public void doesntTruncateTraceIdByDefault() throws Exception { + es.enqueue(new MockResponse()); + spanStore.getTrace("48fec942f3e78b893041d36dc43227fd").execute(); + + assertThat(es.takeRequest().getBody().readUtf8()) + .contains("\"traceId\":\"48fec942f3e78b893041d36dc43227fd\""); + } + + @Test public void truncatesTraceIdTo16CharsWhenNotStrict() throws Exception { + storage = storage.toBuilder().strictTraceId(false).build(); + spanStore = new ElasticsearchHttpSpanStore(storage); + + es.enqueue(new MockResponse()); + spanStore.getTrace("48fec942f3e78b893041d36dc43227fd").execute(); + + assertThat(es.takeRequest().getBody().readUtf8()) + .contains("\"traceId\":\"3041d36dc43227fd\""); + } + @Test public void serviceNames_defaultsTo24HrsAgo_6x() throws Exception { es.enqueue(new MockResponse().setBody(SERVICE_NAMES)); spanStore.getServiceNames().execute(); 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 262c7e465e5..d4a77cdc6b0 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 @@ -21,7 +21,6 @@ import zipkin.Endpoint; import zipkin.TestObjects; import zipkin.internal.ApplyTimestampAndDuration; -import zipkin.internal.Util; import zipkin.internal.V2SpanConverter; import zipkin.internal.v2.Span; import zipkin.internal.v2.codec.Encoder; @@ -147,7 +146,7 @@ public void span_roundTrip() throws IOException { 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(1L).id(1L) + Span worstSpanInTheWorld = Span.builder().traceId("1").id("1") // name is terrible .name(new String(new char[] {'"', '\\', '\t', '\b', '\n', '\r', '\f'})) .localEndpoint(e) @@ -226,7 +225,7 @@ public void span_readsTraceIdHighFromTraceIdField() throws IOException { assertThat(JsonAdapters.SPAN_ADAPTER.fromJson(with128BitTraceId)) .isEqualTo(JsonAdapters.SPAN_ADAPTER.fromJson(withLower64bitsTraceId).toBuilder() - .traceIdHigh(Util.lowerHexToUnsignedLong("48485a3953bb6124")).build()); + .traceId("48485a3953bb61246b221d5bc9e6496c").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 5d6f98af4b6..a5d9c62a7e5 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 @@ -30,6 +30,7 @@ import static zipkin.Constants.SERVER_ADDR; import static zipkin.Constants.SERVER_RECV; import static zipkin.internal.Util.equal; +import static zipkin.internal.Util.toLowerHex; import static zipkin.storage.mysql.internal.generated.tables.ZipkinAnnotations.ZIPKIN_ANNOTATIONS; /** @@ -37,9 +38,8 @@ * short-cuts to require less data. For example, it folds shared RPC spans into one, and doesn't * include tags, non-core annotations or time units. * - *

Out-of-date schemas may be missing the trace_id_high field. When present, this becomes {@link - * Span#traceIdHigh()} used as the left-most 16 characters of the traceId in logging - * statements. + *

Out-of-date schemas may be missing the trace_id_high field. When present, the {@link + * Span#traceId()} could be 32 characters in logging statements. */ final class DependencyLinkV2SpanIterator implements Iterator { @@ -132,11 +132,11 @@ public Span next() { // Skip the client side, so it isn't mistaken for a loopback request if (equal(saService, caService)) caService = null; + Long parentId = row.getValue(ZipkinSpans.ZIPKIN_SPANS.PARENT_ID); Span.Builder result = Span.builder() - .traceIdHigh(traceIdHi != null ? traceIdHi : 0L) - .traceId(traceIdLo) - .parentId(row.getValue(ZipkinSpans.ZIPKIN_SPANS.PARENT_ID)) - .id(spanId); + .traceId(toLowerHex(traceIdHi != null ? traceIdHi : 0L, traceIdLo)) + .parentId(parentId != null ? toLowerHex(parentId) : null) + .id(toLowerHex(spanId)); if (error) { result.putTag(Constants.ERROR, "" /* actual value doesn't matter */); diff --git a/zipkin/src/main/java/zipkin/internal/CorrectForClockSkew.java b/zipkin/src/main/java/zipkin/internal/CorrectForClockSkew.java index f96174fc02d..bfb4d8aeaff 100644 --- a/zipkin/src/main/java/zipkin/internal/CorrectForClockSkew.java +++ b/zipkin/src/main/java/zipkin/internal/CorrectForClockSkew.java @@ -72,7 +72,13 @@ static List apply(Logger logger, List spans) { } rootSpanId = next.id; } - if (!treeBuilder.addNode(next.parentId, next.id, next)) dataError = true; + if (!treeBuilder.addNode( + next.parentId != null ? toLowerHex(next.parentId) : null, + toLowerHex(next.id), + next + )) { + dataError = true; + } } if (rootSpanId == null) { diff --git a/zipkin/src/main/java/zipkin/internal/DependencyLinker.java b/zipkin/src/main/java/zipkin/internal/DependencyLinker.java index 1666cb2d211..185efa461e6 100644 --- a/zipkin/src/main/java/zipkin/internal/DependencyLinker.java +++ b/zipkin/src/main/java/zipkin/internal/DependencyLinker.java @@ -100,7 +100,7 @@ public DependencyLinker putTrace(Iterator spans) { Span first = spans.next(); Node.TreeBuilder builder = - new Node.TreeBuilder<>(logger, MERGE_RPC, first.traceIdString()); + new Node.TreeBuilder<>(logger, MERGE_RPC, first.traceId()); builder.addNode(first.parentId(), first.id(), first); while (spans.hasNext()) { Span next = spans.next(); @@ -192,7 +192,7 @@ public DependencyLinker putTrace(Iterator spans) { // When an RPC is split between spans, we skip the child (server side). If our parent is a // client, we need to check it for errors. if (!isError && Kind.CLIENT.equals(rpcAncestor.kind()) && - currentSpan.parentId() != null && currentSpan.parentId() == rpcAncestor.id()) { + currentSpan.parentId() != null && currentSpan.parentId().equals(rpcAncestor.id())) { isError = rpcAncestor.tags().containsKey(ERROR); } } diff --git a/zipkin/src/main/java/zipkin/internal/Node.java b/zipkin/src/main/java/zipkin/internal/Node.java index 1b5844a7c26..f5dc6552839 100644 --- a/zipkin/src/main/java/zipkin/internal/Node.java +++ b/zipkin/src/main/java/zipkin/internal/Node.java @@ -141,29 +141,29 @@ static final class TreeBuilder { this.traceId = traceId; } - Long rootId = null; + String rootId = null; Node rootNode = null; // Nodes representing the trace tree - Map> idToNode = new LinkedHashMap<>(); + Map> idToNode = new LinkedHashMap<>(); // Collect the parent-child relationships between all spans. - Map idToParent = new LinkedHashMap<>(idToNode.size()); + Map idToParent = new LinkedHashMap<>(idToNode.size()); /** Returns false after logging to FINE if the value couldn't be added */ - public boolean addNode(@Nullable Long parentId, long id, V value) { + public boolean addNode(@Nullable String parentId, String id, V value) { if (parentId == null) { if (rootId != null) { if (logger.isLoggable(FINE)) { logger.fine(format( "attributing span missing parent to root: traceId=%s, rootSpanId=%s, spanId=%s", - traceId, toLowerHex(rootId), toLowerHex(id))); + traceId, rootId, id)); } } else { rootId = id; } - } else if (parentId == id) { + } else if (parentId.equals(id)) { if (logger.isLoggable(FINE)) { logger.fine( - format("skipping circular dependency: traceId=%s, spanId=%s", traceId, toLowerHex(id))); + format("skipping circular dependency: traceId=%s, spanId=%s", traceId, id)); } return false; } @@ -174,7 +174,7 @@ public boolean addNode(@Nullable Long parentId, long id, V value) { if (parentId == null && rootNode == null) { rootNode = node; rootId = id; - } else if (parentId == null && rootId == id) { + } else if (parentId == null && rootId.equals(id)) { rootNode.value(mergeFunction.merge(rootNode.value, node.value)); } else { Node previous = idToNode.put(id, node); @@ -187,7 +187,7 @@ public boolean addNode(@Nullable Long parentId, long id, V value) { /** Builds a tree from calls to {@link #addNode}, or returns an empty tree. */ public Node build() { // Materialize the tree using parent - child relationships - for (Map.Entry entry : idToParent.entrySet()) { + for (Map.Entry entry : idToParent.entrySet()) { Node node = idToNode.get(entry.getKey()); Node parent = idToNode.get(entry.getValue()); if (parent == null) { // handle headless diff --git a/zipkin/src/main/java/zipkin/internal/V2Collector.java b/zipkin/src/main/java/zipkin/internal/V2Collector.java index 683df1f99a2..5f9e0c75a4c 100644 --- a/zipkin/src/main/java/zipkin/internal/V2Collector.java +++ b/zipkin/src/main/java/zipkin/internal/V2Collector.java @@ -45,7 +45,7 @@ public void acceptSpans(byte[] serializedSpans, Decoder decoder, Callback< } @Override protected boolean isSampled(Span span) { - return sampler.isSampled(span.traceId(), span.debug()); + return sampler.isSampled(Util.lowerHexToUnsignedLong(span.traceId()), span.debug()); } @Override protected void record(List sampled, Callback callback) { @@ -53,6 +53,6 @@ public void acceptSpans(byte[] serializedSpans, Decoder decoder, Callback< } @Override protected String idString(Span span) { - return span.idString(); + return span.traceId() + "/" + span.id(); } } diff --git a/zipkin/src/main/java/zipkin/internal/V2SpanConverter.java b/zipkin/src/main/java/zipkin/internal/V2SpanConverter.java index 9634f3de46c..4b235926049 100644 --- a/zipkin/src/main/java/zipkin/internal/V2SpanConverter.java +++ b/zipkin/src/main/java/zipkin/internal/V2SpanConverter.java @@ -30,6 +30,7 @@ import static zipkin.Constants.CLIENT_ADDR; import static zipkin.Constants.LOCAL_COMPONENT; import static zipkin.Constants.SERVER_ADDR; +import static zipkin.internal.Util.lowerHexToUnsignedLong; import static zipkin.internal.Util.writeBase64Url; /** @@ -289,23 +290,26 @@ static boolean closeEnough(Endpoint left, Endpoint right) { static Span.Builder newBuilder(zipkin.Span source) { return Span.builder() - .traceIdHigh(source.traceIdHigh) - .traceId(source.traceId) - .parentId(source.parentId) - .id(source.id) + .traceId(source.traceIdString()) + .parentId(source.parentId != null ? Util.toLowerHex(source.parentId) : null) + .id(Util.toLowerHex(source.id)) .name(source.name) .debug(source.debug); } /** Converts the input, parsing {@link Span#kind()} into RPC annotations. */ public static zipkin.Span toSpan(Span in) { + String traceId = in.traceId(); zipkin.Span.Builder result = zipkin.Span.builder() - .traceIdHigh(in.traceIdHigh()) - .traceId(in.traceId()) - .parentId(in.parentId()) - .id(in.id()) + .traceId(lowerHexToUnsignedLong(traceId)) + .parentId(in.parentId() != null ? lowerHexToUnsignedLong(in.parentId()) : null) + .id(lowerHexToUnsignedLong(in.id())) .debug(in.debug()) - .name(in.name() == null ? "" : in.name()); // avoid a NPE + .name(in.name() != null ? in.name() : ""); // avoid a NPE + + if (traceId.length() == 32) { + result.traceIdHigh(lowerHexToUnsignedLong(traceId, 0)); + } long timestamp = in.timestamp() == null ? 0L : in.timestamp(); long duration = in.duration() == null ? 0L : in.duration(); diff --git a/zipkin/src/main/java/zipkin/internal/V2SpanStoreAdapter.java b/zipkin/src/main/java/zipkin/internal/V2SpanStoreAdapter.java index ba871bab881..f2f8ed8c3e3 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.toLowerHex; final class V2SpanStoreAdapter implements zipkin.storage.SpanStore, AsyncSpanStore { final SpanStore delegate; @@ -69,7 +70,7 @@ public void getTrace(long traceIdHigh, long traceIdLow, Callback> getTraceCall(long traceIdHigh, long traceIdLow) { - return delegate.getTrace(traceIdHigh, traceIdLow).map(getTraceMapper); + return delegate.getTrace(toLowerHex(traceIdHigh, traceIdLow)).map(getTraceMapper); } @Nullable @Override public List getRawTrace(long traceIdHigh, long traceIdLow) { @@ -87,7 +88,7 @@ public void getRawTrace(long traceIdHigh, long traceIdLow, } Call> getRawTraceCall(long traceIdHigh, long traceIdLow) { - return delegate.getTrace(traceIdHigh, traceIdLow).map(getRawTraceMapper); + return delegate.getTrace(toLowerHex(traceIdHigh, traceIdLow)).map(getRawTraceMapper); } @Override public List getServiceNames() { diff --git a/zipkin/src/main/java/zipkin/internal/v2/Span.java b/zipkin/src/main/java/zipkin/internal/v2/Span.java index c0de37eb1b0..43ef0a588fe 100644 --- a/zipkin/src/main/java/zipkin/internal/v2/Span.java +++ b/zipkin/src/main/java/zipkin/internal/v2/Span.java @@ -32,9 +32,7 @@ import static zipkin.internal.Util.UTF_8; import static zipkin.internal.Util.checkNotNull; -import static zipkin.internal.Util.lowerHexToUnsignedLong; import static zipkin.internal.Util.sortedList; -import static zipkin.internal.Util.writeHexLong; /** * A trace is a series of spans (often RPC calls) which form a latency tree. @@ -60,21 +58,32 @@ public abstract class Span implements Serializable { // for Spark jobs private static final long serialVersionUID = 0L; - /** When non-zero, the trace containing this span uses 128-bit trace identifiers. */ - public abstract long traceIdHigh(); - - /** Unique 8-byte identifier for a trace, set on all spans within it. */ - public abstract long traceId(); + /** + * Trace identifier, set on all spans within it. + * + *

Encoded as 16 or 32 lowercase hex characters corresponding to 64 or 128 bits. For example, a + * 128bit trace ID looks like {@code 4e441824ec2b6a44ffdc9bb9a6453df3}. + * + *

Some systems downgrade trace identifiers to 64bit by dropping the left-most 16 characters. + * For example, {@code 4e441824ec2b6a44ffdc9bb9a6453df3} becomes {@code ffdc9bb9a6453df3}. + */ + public abstract String traceId(); - /** The parent's {@link #id} or null if this the root span in a trace. */ - @Nullable public abstract Long parentId(); + /** + * The parent's {@link #id} or null if this the root span in a trace. + * + *

This is the same encoding as {@link #id}. For example {@code ffdc9bb9a6453df3} + */ + @Nullable public abstract String parentId(); /** - * Unique 8-byte identifier of this span within a trace. + * Unique 64bit identifier for this operation within the trace. + * + *

Encoded as 16 lowercase hex characters. For example {@code ffdc9bb9a6453df3} * *

A span is uniquely identified in storage by ({@linkplain #traceId}, {@linkplain #id()}). */ - public abstract long id(); + public abstract String id(); /** Indicates the primary span type. */ public enum Kind { @@ -201,40 +210,6 @@ public enum Kind { : null; } - /** Returns the hex representation of the span's trace ID */ - public String traceIdString() { - if (traceIdHigh() != 0) { - char[] result = new char[32]; - writeHexLong(result, 0, traceIdHigh()); - writeHexLong(result, 16, traceId()); - return new String(result); - } - char[] result = new char[16]; - writeHexLong(result, 0, traceId()); - return new String(result); - } - - /** Returns {@code $traceId.$spanId<:$parentId or $spanId} */ - public String idString() { - int resultLength = (3 * 16) + 3; // 3 ids and the constant delimiters - if (traceIdHigh() != 0) resultLength += 16; - char[] result = new char[resultLength]; - int pos = 0; - if (traceIdHigh() != 0) { - writeHexLong(result, pos, traceIdHigh()); - pos += 16; - } - writeHexLong(result, pos, traceId()); - pos += 16; - result[pos++] = '.'; - writeHexLong(result, pos, id()); - pos += 16; - result[pos++] = '<'; - result[pos++] = ':'; - writeHexLong(result, pos, parentId() != null ? parentId() : id()); - return new String(result); - } - public static Builder builder() { return new Builder(); } @@ -244,10 +219,9 @@ public Builder toBuilder() { } public static final class Builder { - Long traceId; - long traceIdHigh; - Long parentId; - Long id; + String traceId; + String parentId; + String id; Kind kind; String name; Long timestamp; @@ -260,7 +234,6 @@ public static final class Builder { Boolean shared; public Builder clear() { - traceIdHigh = 0L; traceId = null; parentId = null; id = null; @@ -279,7 +252,6 @@ public Builder clear() { @Override public Builder clone() { Builder result = new Builder(); - result.traceIdHigh = traceIdHigh; result.traceId = traceId; result.parentId = parentId; result.id = id; @@ -331,60 +303,38 @@ public Builder clear() { } /** - * Decodes the trace ID from its lower-hex representation. - * - *

Use this instead decoding yourself and calling {@link #traceIdHigh(long)} and {@link - * #traceId(long)} + * @throws IllegalArgumentException if not lower-hex format + * @see Span#id() */ public Builder traceId(String traceId) { - checkNotNull(traceId, "traceId"); - if (traceId.length() == 32) { - traceIdHigh(lowerHexToUnsignedLong(traceId, 0)); - } - return traceId(lowerHexToUnsignedLong(traceId)); - } - - /** @see Span#traceIdHigh */ - public Builder traceIdHigh(long traceIdHigh) { - this.traceIdHigh = traceIdHigh; - return this; - } - - /** @see Span#traceId */ - public Builder traceId(long traceId) { - this.traceId = traceId; + this.traceId = normalizeTraceId(traceId); return this; } /** - * Decodes the parent ID from its lower-hex representation. - * - *

Use this instead decoding yourself and calling {@link #parentId(Long)} + * @throws IllegalArgumentException if not lower-hex format + * @see Span#parentId() */ public Builder parentId(@Nullable String parentId) { - this.parentId = parentId != null ? lowerHexToUnsignedLong(parentId) : null; - return this; - } - - /** @see Span#parentId */ - public Builder parentId(@Nullable Long parentId) { - this.parentId = parentId; + if (parentId != null) { + int length = parentId.length(); + if (length > 16) throw new IllegalArgumentException("parentId.length > 16"); + validateHex(parentId); + this.parentId = length < 16 ? padLeft(parentId, 16) : parentId; + } return this; } /** - * Decodes the span ID from its lower-hex representation. - * - *

Use this instead decoding yourself and calling {@link #id(long)} + * @throws IllegalArgumentException if not lower-hex format + * @see Span#id() */ public Builder id(String id) { - this.id = lowerHexToUnsignedLong(id); - return this; - } - - /** @see Span#id */ - public Builder id(long id) { - this.id = id; + if (id == null) throw new NullPointerException("id == null"); + int length = id.length(); + if (length > 16) throw new IllegalArgumentException("id.length > 16"); + validateHex(id); + this.id = length < 16 ? padLeft(id, 16) : id; return this; } @@ -454,7 +404,6 @@ public Builder shared(@Nullable Boolean shared) { public Span build() { return new AutoValue_Span( - traceIdHigh, traceId, parentId, id, @@ -478,4 +427,41 @@ public Span build() { @Override public String toString() { return new String(Encoder.JSON.encode(this), UTF_8); } + + /** + * Returns a valid lower-hex trace ID, padded left as needed to 16 or 32 characters. + * + * @throws IllegalArgumentException if oversized or not lower-hex + */ + public static String normalizeTraceId(String traceId) { + if (traceId == null) throw new NullPointerException("traceId == null"); + int length = traceId.length(); + if (length > 32) throw new IllegalArgumentException("traceId.length > 32"); + validateHex(traceId); + if (length == 32 || length == 16) { + return traceId; + } else if (length < 16) { + return padLeft(traceId, 16); + } else { + return padLeft(traceId, 32); + } + } + + static String padLeft(String id, int desiredLength) { + StringBuilder builder = new StringBuilder(desiredLength); + int offset = desiredLength - id.length(); + + for (int i = 0; i < offset; i++) builder.append('0'); + builder.append(id); + return builder.toString(); + } + + static void validateHex(String id) { + for (int i = 0, length = id.length(); i < length; i++) { + char c = id.charAt(i); + if ((c < '0' || c > '9') && (c < 'a' || c > 'f')) { + throw new IllegalArgumentException(id + " should be lower-hex encoded with no prefix"); + } + } + } } 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 97d397440b1..3c5881bf946 100644 --- a/zipkin/src/main/java/zipkin/internal/v2/codec/Span2JsonAdapters.java +++ b/zipkin/src/main/java/zipkin/internal/v2/codec/Span2JsonAdapters.java @@ -214,8 +214,7 @@ static final class Span2Reader implements JsonReaderAdapter { static final Buffer.Writer SPAN_WRITER = new Buffer.Writer() { @Override public int sizeInBytes(Span value) { int sizeInBytes = 0; - if (value.traceIdHigh() != 0) sizeInBytes += 16; - sizeInBytes += "{\"traceId\":\"".length() + 16 + 1; + sizeInBytes += "{\"traceId\":\"".length() + value.traceId().length() + 1; if (value.parentId() != null) { sizeInBytes += ",\"parentId\":\"".length() + 16 + 1; } @@ -274,15 +273,11 @@ static final class Span2Reader implements JsonReaderAdapter { } @Override public void write(Span value, Buffer b) { - b.writeAscii("{\"traceId\":\""); - if (value.traceIdHigh() != 0) { - b.writeLowerHex(value.traceIdHigh()); - } - b.writeLowerHex(value.traceId()).writeByte('"'); + b.writeAscii("{\"traceId\":\"").writeAscii(value.traceId()).writeByte('"'); if (value.parentId() != null) { - b.writeAscii(",\"parentId\":\"").writeLowerHex(value.parentId()).writeByte('"'); + b.writeAscii(",\"parentId\":\"").writeAscii(value.parentId()).writeByte('"'); } - b.writeAscii(",\"id\":\"").writeLowerHex(value.id()).writeByte('"'); + b.writeAscii(",\"id\":\"").writeAscii(value.id()).writeByte('"'); if (value.kind() != null) { b.writeAscii(",\"kind\":\"").writeJsonEscaped(value.kind().toString()).writeByte('"'); } 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 5c9b343326e..a2d85cb08a3 100644 --- a/zipkin/src/main/java/zipkin/internal/v2/storage/InMemoryStorage.java +++ b/zipkin/src/main/java/zipkin/internal/v2/storage/InMemoryStorage.java @@ -30,10 +30,10 @@ 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.GroupByTraceId.TRACE_DESCENDING; import static zipkin.internal.Util.sortedList; /** @@ -147,18 +147,19 @@ public synchronized void clear() { evictToRecoverSpans(spansToRecover); for (Span span : spans) { Long timestamp = span.timestamp() != null ? span.timestamp() : Long.MIN_VALUE; - Pair traceIdTimeStamp = Pair.create(span.traceId(), timestamp); + long traceId = Util.lowerHexToUnsignedLong(span.traceId()); + Pair traceIdTimeStamp = Pair.create(traceId, timestamp); spansByTraceIdTimeStamp.put(traceIdTimeStamp, span); - traceIdToTraceIdTimeStamps.put(span.traceId(), traceIdTimeStamp); + traceIdToTraceIdTimeStamps.put(traceId, traceIdTimeStamp); acceptedSpanCount++; String spanName = span.name(); if (span.localServiceName() != null) { - serviceToTraceIds.put(span.localServiceName(), span.traceId()); + serviceToTraceIds.put(span.localServiceName(), traceId); serviceToSpanNames.put(span.localServiceName(), spanName); } if (span.remoteServiceName() != null) { - serviceToTraceIds.put(span.remoteServiceName(), span.traceId()); + serviceToTraceIds.put(span.remoteServiceName(), traceId); serviceToSpanNames.put(span.remoteServiceName(), spanName); } } @@ -222,13 +223,13 @@ synchronized Call>> getTraces(QueryRequest request, boolean stri } static Collection> strictByTraceId(List next) { - Map, List> groupedByTraceId = new LinkedHashMap<>(); + Map> groupedByTraceId = new LinkedHashMap<>(); for (Span span : next) { - Pair traceIdPair = Pair.create(span.traceIdHigh(), span.traceId()); - if (!groupedByTraceId.containsKey(traceIdPair)) { - groupedByTraceId.put(traceIdPair, new LinkedList<>()); + String traceId = span.traceId(); + if (!groupedByTraceId.containsKey(traceId)) { + groupedByTraceId.put(traceId, new LinkedList<>()); } - groupedByTraceId.get(traceIdPair).add(span); + groupedByTraceId.get(traceId).add(span); } return groupedByTraceId.values(); } @@ -265,15 +266,16 @@ Set traceIdsDescendingByTimestamp(QueryRequest request) { return result; } - @Override public synchronized Call> getTrace(long traceIdHigh, long traceId) { - List spans = spansByTraceId(traceId); + @Override public synchronized Call> getTrace(String traceId) { + traceId = Span.normalizeTraceId(traceId); + List spans = spansByTraceId(Util.lowerHexToUnsignedLong(traceId)); if (spans == null || spans.isEmpty()) return Call.emptyList(); if (!strictTraceId) return Call.create(spans); List filtered = new ArrayList<>(spans); Iterator iterator = filtered.iterator(); while (iterator.hasNext()) { - if (iterator.next().traceIdHigh() != traceIdHigh) { + if (!iterator.next().traceId().equals(traceId)) { iterator.remove(); } } @@ -385,9 +387,9 @@ Collection get(K key) { } } - private List spansByTraceId(long traceId) { + private List spansByTraceId(long lowTraceId) { List sameTraceId = new ArrayList<>(); - for (Pair traceIdTimestamp : traceIdToTraceIdTimeStamps.get(traceId)) { + for (Pair traceIdTimestamp : traceIdToTraceIdTimeStamps.get(lowTraceId)) { sameTraceId.addAll(spansByTraceIdTimeStamp.get(traceIdTimestamp)); } return sameTraceId; @@ -401,14 +403,4 @@ private Collection> traceIdTimestampsByServiceName(String serviceName Collections.sort(traceIdTimestamps, VALUE_2_DESCENDING); return traceIdTimestamps; } - - /** Compares by {@link Span#timestamp()} if present. */ - final static Comparator SPAN_COMPARATOR = new Comparator() { - @Override public int compare(Span left, Span right) { - if (left == right) return 0; - long x = left.timestamp() == null ? Long.MIN_VALUE : left.timestamp(); - long y = right.timestamp() == null ? Long.MIN_VALUE : right.timestamp(); - return x < y ? -1 : x == y ? 0 : 1; - } - }; } diff --git a/zipkin/src/main/java/zipkin/internal/v2/storage/SpanStore.java b/zipkin/src/main/java/zipkin/internal/v2/storage/SpanStore.java index 11b6971a47e..25ef0d73316 100644 --- a/zipkin/src/main/java/zipkin/internal/v2/storage/SpanStore.java +++ b/zipkin/src/main/java/zipkin/internal/v2/storage/SpanStore.java @@ -41,12 +41,13 @@ public interface SpanStore { * found. * *

When {@link StorageComponent.Builder#strictTraceId(boolean)} is true, spans with the same - * {@code traceIdLow} are returned even if the {@code traceIdHigh is different}. + * right-most 16 characters are returned even if the characters to the left are not. * - * @param traceIdHigh The upper 64-bits of the trace ID. See {@link Span#traceIdHigh()} - * @param traceIdLow The lower 64-bits of the trace ID. See {@link Span#traceId()} + *

Implementations should use {@link Span#normalizeTraceId(String)} to ensure consistency. + * + * @param traceId the {@link Span#traceId() trace ID} */ - Call> getTrace(long traceIdHigh, long traceIdLow); + Call> getTrace(String traceId); /** * Retrieves all {@link Span#localEndpoint() local} and {@link Span#remoteEndpoint() remote} diff --git a/zipkin/src/test/java/zipkin/internal/DependencyLinkerTest.java b/zipkin/src/test/java/zipkin/internal/DependencyLinkerTest.java index 42f06e6ff13..0c764bc50f4 100644 --- a/zipkin/src/test/java/zipkin/internal/DependencyLinkerTest.java +++ b/zipkin/src/test/java/zipkin/internal/DependencyLinkerTest.java @@ -29,6 +29,7 @@ import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static zipkin.Constants.ERROR; +import static zipkin.internal.Util.toLowerHex; public class DependencyLinkerTest { List messages = new ArrayList<>(); @@ -467,7 +468,7 @@ public void doesntLinkUnrelatedSpansWhenMissingRootSpan() { @Test public void linksRelatedSpansWhenMissingRootSpan() { - long missingParentId = 1; + long missingParentId = 1L; List trace = asList( span2(1L, missingParentId, 2L, Kind.SERVER, "service1", null, false), span2(1L, 2L, 3L, Kind.SERVER, "service2", null, false) @@ -554,8 +555,11 @@ 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(); - result.traceId(traceId).parentId(parentId).id(id).kind(kind); + Span.Builder result = Span.builder() + .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 (isError) result.putTag(ERROR, ""); diff --git a/zipkin/src/test/java/zipkin/internal/NodeTest.java b/zipkin/src/test/java/zipkin/internal/NodeTest.java index 06fa25e97ce..fba3b733c78 100644 --- a/zipkin/src/test/java/zipkin/internal/NodeTest.java +++ b/zipkin/src/test/java/zipkin/internal/NodeTest.java @@ -25,6 +25,7 @@ import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; +import static zipkin.internal.Util.toLowerHex; public class NodeTest { List messages = new ArrayList<>(); @@ -97,7 +98,9 @@ public void constructsTraceTree() { Node.TreeBuilder treeBuilder = new Node.TreeBuilder<>(logger, copy.get(0).traceIdString()); for (Span span : copy) { - treeBuilder.addNode(span.parentId, span.id, span); + treeBuilder.addNode( + span.parentId != null ? toLowerHex(span.parentId) : null, toLowerHex(span.id), span + ); } Node root = treeBuilder.build(); assertThat(root.value()) @@ -123,8 +126,9 @@ public void constructTree_noChildLeftBehind() { Node.TreeBuilder treeBuilder = new Node.TreeBuilder<>(logger, spans.get(0).traceIdString()); for (Span span : spans) { - assertThat(treeBuilder.addNode(span.parentId, span.id, span)) - .isTrue(); + assertThat(treeBuilder.addNode( + span.parentId != null ? toLowerHex(span.parentId) : null, toLowerHex(span.id), span + )).isTrue(); } Node tree = treeBuilder.build(); Iterator> iter = tree.traverse(); @@ -146,7 +150,9 @@ public void constructTree_noChildLeftBehind() { Node.TreeBuilder treeBuilder = new Node.TreeBuilder<>(logger, s2.traceIdString()); for (Span span : asList(s2, s3, s4)) { - treeBuilder.addNode(span.parentId, span.id, span); + treeBuilder.addNode( + span.parentId != null ? toLowerHex(span.parentId) : null, toLowerHex(span.id), span + ); } Node root = treeBuilder.build(); assertThat(root.isSyntheticRootForPartialTree()) @@ -164,8 +170,12 @@ public void addNode_skipsOnCycle() { Span s2 = Span.builder().traceId(137L).parentId(2L).id(2L).name("s2").build(); Node.TreeBuilder treeBuilder = new Node.TreeBuilder<>(logger, s2.traceIdString()); - treeBuilder.addNode(s1.parentId, s1.id, s1); - assertThat(treeBuilder.addNode(s2.parentId, s2.id, s2)).isFalse(); + treeBuilder.addNode( + s1.parentId != null ? toLowerHex(s1.parentId) : null, toLowerHex(s1.id), s1 + ); + assertThat(treeBuilder.addNode( + s2.parentId != null ? toLowerHex(s2.parentId) : null, toLowerHex(s2.id), s2 + )).isFalse(); treeBuilder.build(); assertThat(messages).containsExactly( diff --git a/zipkin/src/test/java/zipkin/internal/V2SpanConverterTest.java b/zipkin/src/test/java/zipkin/internal/V2SpanConverterTest.java index c84fb6e8e7e..bcdcce21905 100644 --- a/zipkin/src/test/java/zipkin/internal/V2SpanConverterTest.java +++ b/zipkin/src/test/java/zipkin/internal/V2SpanConverterTest.java @@ -230,9 +230,9 @@ 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() - .traceId(1L) - .parentId(1L) - .id(2L) + .traceId("1") + .parentId("1") + .id("2") .name("foo") .timestamp(1472470996199000L) .duration(207000L) @@ -255,9 +255,9 @@ public class V2SpanConverterTest { /** No special treatment for invalid core annotations: missing endpoint */ @Test public void missingEndpoints_coreAnnotation() { Span span2 = Span.builder() - .traceId(1L) - .parentId(1L) - .id(2L) + .traceId("1") + .parentId("1") + .id("2") .name("foo") .timestamp(1472470996199000L) .addAnnotation(1472470996199000L, "sr") @@ -280,9 +280,9 @@ public class V2SpanConverterTest { @Test public void localSpan_emptyComponent() { Span simpleLocal = Span.builder() - .traceId(1L) - .parentId(1L) - .id(2L) + .traceId("1") + .parentId("1") + .id("2") .name("local") .localEndpoint(frontend) .timestamp(1472470996199000L) @@ -777,9 +777,9 @@ public class V2SpanConverterTest { .build(); Span span2 = Span.builder() - .traceId(1) + .traceId("1") .name("test") - .id(2) + .id("2") .localEndpoint(frontend) .putTag("bool", "true") .putTag("short", "20") diff --git a/zipkin/src/test/java/zipkin/internal/V2SpanStoreAdapterTest.java b/zipkin/src/test/java/zipkin/internal/V2SpanStoreAdapterTest.java index 2fe6cafb83d..5a6541d164b 100644 --- a/zipkin/src/test/java/zipkin/internal/V2SpanStoreAdapterTest.java +++ b/zipkin/src/test/java/zipkin/internal/V2SpanStoreAdapterTest.java @@ -157,7 +157,7 @@ public void getTraces_sync_wrapsIOE() throws IOException { } @Test public void getTrace_sync_callsExecute() throws IOException { - when(spanStore.getTrace(3L, 4L)) + when(spanStore.getTrace("00000000000000030000000000000004")) .thenReturn(call); when(call.execute()) .thenReturn(Collections.emptyList()); @@ -170,7 +170,7 @@ public void getTraces_sync_wrapsIOE() throws IOException { @Test(expected = UncheckedIOException.class) public void getTrace_sync_wrapsIOE() throws IOException { - when(spanStore.getTrace(3L, 4L)) + when(spanStore.getTrace("00000000000000030000000000000004")) .thenReturn(call); when(call.execute()) .thenThrow(IOException.class); @@ -179,7 +179,7 @@ public void getTrace_sync_wrapsIOE() throws IOException { } @Test public void getTrace_async_callsEnqueue() { - when(spanStore.getTrace(3L, 4L)) + when(spanStore.getTrace("00000000000000030000000000000004")) .thenReturn(call); doEnqueue(c -> c.onSuccess(Collections.emptyList())); @@ -190,7 +190,7 @@ public void getTrace_sync_wrapsIOE() throws IOException { @Test public void getTrace_async_doesntWrapIOE() { IOException throwable = new IOException(); - when(spanStore.getTrace(3L, 4L)) + when(spanStore.getTrace("00000000000000030000000000000004")) .thenReturn(call); doEnqueue(c -> c.onError(throwable)); @@ -200,7 +200,7 @@ public void getTrace_sync_wrapsIOE() throws IOException { } @Test public void getRawTrace_sync_callsExecute() throws IOException { - when(spanStore.getTrace(3L, 4L)) + when(spanStore.getTrace("00000000000000030000000000000004")) .thenReturn(call); when(call.execute()) .thenReturn(Collections.emptyList()); @@ -213,7 +213,7 @@ public void getTrace_sync_wrapsIOE() throws IOException { @Test(expected = UncheckedIOException.class) public void getRawTrace_sync_wrapsIOE() throws IOException { - when(spanStore.getTrace(3L, 4L)) + when(spanStore.getTrace("00000000000000030000000000000004")) .thenReturn(call); when(call.execute()) .thenThrow(IOException.class); @@ -222,7 +222,7 @@ public void getRawTrace_sync_wrapsIOE() throws IOException { } @Test public void getRawTrace_async_callsEnqueue() { - when(spanStore.getTrace(3L, 4L)) + when(spanStore.getTrace("00000000000000030000000000000004")) .thenReturn(call); doEnqueue(c -> c.onSuccess(Collections.emptyList())); @@ -233,7 +233,7 @@ public void getRawTrace_sync_wrapsIOE() throws IOException { @Test public void getRawTrace_async_doesntWrapIOE() { IOException throwable = new IOException(); - when(spanStore.getTrace(3L, 4L)) + when(spanStore.getTrace("00000000000000030000000000000004")) .thenReturn(call); doEnqueue(c -> c.onError(throwable)); @@ -378,8 +378,8 @@ public void getDependencies_sync_wrapsIOE() throws IOException { @Test public void getTracesMapper_descendingOrder() { assertThat(V2SpanStoreAdapter.getTracesMapper.map(asList( - asList(builder.traceId(1L).timestamp((TODAY + 1) * 1000).build()), - asList(builder.traceId(2L).timestamp((TODAY + 2) * 1000).build()) + asList(builder.traceId("1").timestamp((TODAY + 1) * 1000).build()), + asList(builder.traceId("2").timestamp((TODAY + 2) * 1000).build()) ))).flatExtracting(s -> s) .extracting(s -> s.timestamp) .containsExactly((TODAY + 2) * 1000, (TODAY + 1) * 1000); diff --git a/zipkin/src/test/java/zipkin/internal/v2/SpanTest.java b/zipkin/src/test/java/zipkin/internal/v2/SpanTest.java index af6e6187843..ac86b766db7 100644 --- a/zipkin/src/test/java/zipkin/internal/v2/SpanTest.java +++ b/zipkin/src/test/java/zipkin/internal/v2/SpanTest.java @@ -25,57 +25,17 @@ import static zipkin.TestObjects.APP_ENDPOINT; public class SpanTest { - Span base = Span.builder().traceId(1L).id(1L).localEndpoint(APP_ENDPOINT).build(); + Span base = Span.builder().traceId("1").id("1").localEndpoint(APP_ENDPOINT).build(); @Test public void traceIdString() { - Span with128BitId = Span.builder() - .traceId(Util.lowerHexToUnsignedLong("48485a3953bb6124")) - .id(1) + Span with128BitId = base.toBuilder() + .traceId("463ac35c9f6413ad48485a3953bb6124") .name("foo").build(); - assertThat(with128BitId.traceIdString()) - .isEqualTo("48485a3953bb6124"); - } - - @Test public void traceIdString_high() { - Span with128BitId = Span.builder() - .traceId(Util.lowerHexToUnsignedLong("48485a3953bb6124")) - .traceIdHigh(Util.lowerHexToUnsignedLong("463ac35c9f6413ad")) - .id(1) - .name("foo").build(); - - assertThat(with128BitId.traceIdString()) + assertThat(with128BitId.traceId()) .isEqualTo("463ac35c9f6413ad48485a3953bb6124"); } - @Test - public void idString_traceIdHigh() { - Span with128BitId = Span.builder() - .traceId(Util.lowerHexToUnsignedLong("48485a3953bb6124")) - .traceIdHigh(Util.lowerHexToUnsignedLong("463ac35c9f6413ad")) - .id(1) - .name("foo").build(); - - assertThat(with128BitId.idString()) - .isEqualTo("463ac35c9f6413ad48485a3953bb6124.0000000000000001<:0000000000000001"); - } - - @Test - public void idString_withParent() { - Span withParent = Span.builder().name("foo").traceId(1).id(3).parentId(2L).build(); - - assertThat(withParent.idString()) - .isEqualTo("0000000000000001.0000000000000003<:0000000000000002"); - } - - @Test - public void idString_noParent() { - Span noParent = Span.builder().name("foo").traceId(1).id(1).build(); - - assertThat(noParent.idString()) - .isEqualTo("0000000000000001.0000000000000001<:0000000000000001"); - } - @Test public void spanNamesLowercase() { assertThat(base.toBuilder().name("GET").build().name()) .isEqualTo("get"); 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 8e54cfe38a2..f9c626c0f2d 100644 --- a/zipkin/src/test/java/zipkin/internal/v2/codec/SpanJsonAdaptersTest.java +++ b/zipkin/src/test/java/zipkin/internal/v2/codec/SpanJsonAdaptersTest.java @@ -67,7 +67,7 @@ public class SpanJsonAdaptersTest { } @Test public void spanRoundTrip_64bitTraceId() throws IOException { - span = span.toBuilder().traceIdHigh(0L).build(); + span = span.toBuilder().traceId(span.traceId().substring(16)).build(); assertThat(Decoder.JSON.decodeList(encodeList(span))) .containsOnly(span); @@ -81,7 +81,7 @@ public class SpanJsonAdaptersTest { } @Test public void sizeInBytes_64bitTraceId() throws IOException { - span = span.toBuilder().traceIdHigh(0L).build(); + span = span.toBuilder().traceId(span.traceId().substring(16)).build(); assertThat(Span2JsonAdapters.SPAN_WRITER.sizeInBytes(span)) .isEqualTo(Encoder.JSON.encode(span).length); @@ -93,7 +93,7 @@ public class SpanJsonAdaptersTest { */ @Test public void specialCharsInJson() throws IOException { // service name is surrounded by control characters - Span worstSpanInTheWorld = Span.builder().traceId(1L).id(1L) + Span worstSpanInTheWorld = Span.builder().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)) @@ -109,8 +109,7 @@ public class SpanJsonAdaptersTest { @Test public void niceErrorOnUppercase_traceId() { thrown.expect(IllegalArgumentException.class); - thrown.expectMessage( - "48485A3953BB6124 should be a 1 to 32 character lower-hex string with no prefix"); + thrown.expectMessage("48485A3953BB6124 should be lower-hex encoded with no prefix"); String json = "[{\n" + " \"traceId\": \"48485A3953BB6124\",\n" @@ -151,10 +150,9 @@ public class SpanJsonAdaptersTest { @Test public void writesTraceIdHighIntoTraceIdField() { Span with128BitTraceId = Span.builder() - .traceIdHigh(Util.lowerHexToUnsignedLong("48485a3953bb6124")) - .traceId(Util.lowerHexToUnsignedLong("6b221d5bc9e6496c")) + .traceId("48485a3953bb61246b221d5bc9e6496c") .localEndpoint(frontend) - .id(1).name("").build(); + .id("1").name("").build(); assertThat(new String(Encoder.JSON.encode(with128BitTraceId), Util.UTF_8)) .startsWith("{\"traceId\":\"48485a3953bb61246b221d5bc9e6496c\""); @@ -174,7 +172,7 @@ public class SpanJsonAdaptersTest { assertThat(Decoder.JSON.decodeList(with128BitTraceId).get(0)) .isEqualTo(Decoder.JSON.decodeList(withLower64bitsTraceId).get(0).toBuilder() - .traceIdHigh(Util.lowerHexToUnsignedLong("48485a3953bb6124")).build()); + .traceId("48485a3953bb61246b221d5bc9e6496c").build()); } @Test public void ignoresNull_topLevelFields() { 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 03a65d9fb83..6125eaa80c8 100644 --- a/zipkin/src/test/java/zipkin/internal/v2/storage/InMemoryStorageTest.java +++ b/zipkin/src/test/java/zipkin/internal/v2/storage/InMemoryStorageTest.java @@ -29,6 +29,7 @@ import static zipkin.TestObjects.APP_ENDPOINT; import static zipkin.TestObjects.DAY; import static zipkin.TestObjects.TODAY; +import static zipkin.internal.Util.toLowerHex; public class InMemoryStorageTest { InMemoryStorage storage = InMemoryStorage.newBuilder().build(); @@ -40,11 +41,13 @@ public class InMemoryStorageTest { long gapBetweenSpans = 100; List earlySpans = IntStream.rangeClosed(1, 10).mapToObj(i -> Span.builder().name("early") - .traceId(i).id(i).timestamp((TODAY - i) * 1000).duration(1L) + .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") - .traceId(i + 10).id(i + 10).timestamp((TODAY + gapBetweenSpans - i) * 1000).duration(1L) + .traceId(toLowerHex(i + 10)).id(toLowerHex(i + 10)) + .timestamp((TODAY + gapBetweenSpans - i) * 1000).duration(1L) .localEndpoint(endpoints.get(i - 1)).build()).collect(toList()); storage.accept(earlySpans).execute(); @@ -77,7 +80,7 @@ public class InMemoryStorageTest { /** It should be safe to run dependency link jobs twice */ @Test public void replayOverwrites() throws IOException { - Span span = Span.builder().traceId(10L).id(10L).name("receive") + Span span = Span.builder().traceId("10").id("10").name("receive") .kind(Span.Kind.CONSUMER) .localEndpoint(APP_ENDPOINT) .remoteEndpoint(Endpoint.builder().serviceName("kafka").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 f71ec9f4537..a33325b504f 100644 --- a/zipkin/src/test/java/zipkin/internal/v2/storage/QueryRequestTest.java +++ b/zipkin/src/test/java/zipkin/internal/v2/storage/QueryRequestTest.java @@ -31,7 +31,7 @@ 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(10L).id(10L).name("receive") + Span span = Span.builder().traceId("10").id("10").name("receive") .localEndpoint(APP_ENDPOINT) .kind(Span.Kind.CONSUMER) .timestamp(TODAY * 1000) @@ -146,7 +146,7 @@ public class QueryRequestTest { .build(); assertThat(request.test(asList( - span.toBuilder().id(2).parentId(span.id()).timestamp(null).build(), + span.toBuilder().id("2").parentId(span.id()).timestamp(null).build(), span ))).isTrue(); } @@ -156,8 +156,8 @@ public class QueryRequestTest { .build(); assertThat(request.test(asList( - span.toBuilder().id(2).parentId(span.id()).timestamp(span.timestamp() + DAY * 1000).build(), - span.toBuilder().id(3).parentId(span.id()).build() + span.toBuilder().id("2").parentId(span.id()).timestamp(span.timestamp() + DAY * 1000).build(), + span.toBuilder().id("3").parentId(span.id()).build() ))).isTrue(); } @@ -227,17 +227,17 @@ public class QueryRequestTest { .isFalse(); } - Span foo = span.toBuilder().traceId(1).name("call1").id(1) + Span foo = span.toBuilder().traceId("1").name("call1").id("1") .addAnnotation(span.timestamp(), "foo").build(); // would be foo bar, except lexicographically bar precedes foo - Span barAndFoo = span.toBuilder().traceId(2).name("call2").id(2) + Span barAndFoo = span.toBuilder().traceId("2").name("call2").id("2") .addAnnotation(span.timestamp(), "bar") .addAnnotation(span.timestamp(), "foo").build(); - Span fooAndBazAndQux = span.toBuilder().traceId(3).name("call3").id(3) + Span fooAndBazAndQux = span.toBuilder().traceId("3").name("call3").id("3") .addAnnotation(span.timestamp(), "foo") .putTag("baz", "qux") .build(); - Span barAndFooAndBazAndQux = span.toBuilder().traceId(4).name("call4").id(4) + Span barAndFooAndBazAndQux = span.toBuilder().traceId("4").name("call4").id("4") .addAnnotation(span.timestamp(), "bar") .addAnnotation(span.timestamp(), "foo") .putTag("baz", "qux") From 458c74acf524c926d6688973b042a5e3fb7efff8 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sat, 2 Sep 2017 11:30:43 +0800 Subject: [PATCH 2/2] Changes v2 IP addresses to strings and fleshed v2 model This adds decoupled Annotation and Endpoint variants for v2. This also fleshes out codec for use in benchmarks, spark and query apis. --- .../zipkin/benchmarks/CodecBenchmarks.java | 30 +- .../benchmarks/Span2ConverterBenchmarks.java | 8 +- .../zipkin/benchmarks/SpanBenchmarks.java | 18 +- .../src/main/resources/span-client.json | 2 +- benchmarks/src/main/resources/span2.json | 8 +- .../collector/kafka/KafkaCollectorTest.java | 9 +- .../collector/kafka10/KafkaCollectorTest.java | 9 +- .../java/zipkin/junit/ZipkinDispatcher.java | 27 +- .../test/java/zipkin/junit/ITHttpStorage.java | 5 - .../java/zipkin/junit/ZipkinRuleTest.java | 9 +- .../zipkin/junit/v2/HttpV2SpanConsumer.java | 4 +- .../java/zipkin/junit/v2/HttpV2SpanStore.java | 6 +- .../zipkin/server/EnableZipkinServer.java | 1 - .../java/zipkin/server/ZipkinQueryApiV2.java | 30 +- .../server/ZipkinServerIntegrationTest.java | 7 +- .../cassandra3/CassandraSpanStore.java | 1 - .../http/ElasticsearchHttpSpanConsumer.java | 14 +- .../elasticsearch/http/HttpBulkIndexer.java | 1 - .../elasticsearch/http/JsonAdapters.java | 46 +- .../http/LegacyJsonAdapters.java | 72 ++- .../ElasticsearchHttpSpanConsumerTest.java | 60 ++- .../elasticsearch/http/InternalForTests.java | 1 - .../elasticsearch/http/JsonAdaptersTest.java | 18 +- .../mysql/DependencyLinkV2SpanIterator.java | 6 +- .../DependencyLinkV2SpanIteratorTest.java | 24 +- .../main/java/zipkin/collector/Collector.java | 4 +- .../zipkin/collector/CollectorMetrics.java | 1 - .../main/java/zipkin/internal/JsonCodec.java | 14 +- .../src/main/java/zipkin/internal/Node.java | 1 - .../java/zipkin/internal/V2Collector.java | 8 +- .../zipkin/internal/V2JsonSpanDecoder.java | 4 +- .../java/zipkin/internal/V2SpanConverter.java | 139 +++--- .../zipkin/internal/V2SpanStoreAdapter.java | 5 +- .../java/zipkin/internal/v2/Annotation.java | 57 +++ .../java/zipkin/internal/v2/Endpoint.java | 438 ++++++++++++++++++ .../main/java/zipkin/internal/v2/Span.java | 47 +- .../codec/{Decoder.java => BytesDecoder.java} | 25 +- .../codec/{Encoder.java => BytesEncoder.java} | 25 +- .../internal/v2/codec/MessageEncoder.java | 54 --- .../internal/v2/codec/Span2JsonAdapters.java | 122 +++-- .../internal/v2/storage/InMemoryStorage.java | 117 ++--- .../internal/v2/storage/QueryRequest.java | 10 +- .../internal/v2/storage/SpanConsumer.java | 1 - .../zipkin/storage/AsyncSpanConsumer.java | 1 - .../java/zipkin/collector/CollectorTest.java | 7 +- .../zipkin/internal/DependencyLinkerTest.java | 8 +- .../internal/DetectingSpanDecoderTest.java | 13 +- .../internal/V2JsonSpanDecoderTest.java | 10 +- .../zipkin/internal/V2SpanConverterTest.java | 119 ++--- .../internal/V2SpanStoreAdapterTest.java | 27 +- .../zipkin/internal/v2/AnnotationTest.java | 36 ++ .../java/zipkin/internal/v2/EndpointTest.java | 195 ++++++++ .../java/zipkin/internal/v2/SpanTest.java | 9 +- .../v2/codec/MessageEncodingTest.java | 43 -- .../v2/codec/SpanJsonAdaptersTest.java | 165 ++++--- .../v2/storage/InMemoryStorageTest.java | 24 +- .../internal/v2/storage/QueryRequestTest.java | 6 +- 57 files changed, 1439 insertions(+), 712 deletions(-) create mode 100644 zipkin/src/main/java/zipkin/internal/v2/Annotation.java create mode 100644 zipkin/src/main/java/zipkin/internal/v2/Endpoint.java rename zipkin/src/main/java/zipkin/internal/v2/codec/{Decoder.java => BytesDecoder.java} (60%) rename zipkin/src/main/java/zipkin/internal/v2/codec/{Encoder.java => BytesEncoder.java} (60%) delete mode 100644 zipkin/src/main/java/zipkin/internal/v2/codec/MessageEncoder.java create mode 100644 zipkin/src/test/java/zipkin/internal/v2/AnnotationTest.java create mode 100644 zipkin/src/test/java/zipkin/internal/v2/EndpointTest.java delete mode 100644 zipkin/src/test/java/zipkin/internal/v2/codec/MessageEncodingTest.java diff --git a/benchmarks/src/main/java/zipkin/benchmarks/CodecBenchmarks.java b/benchmarks/src/main/java/zipkin/benchmarks/CodecBenchmarks.java index 50d8a1e6fdb..197090bac6e 100644 --- a/benchmarks/src/main/java/zipkin/benchmarks/CodecBenchmarks.java +++ b/benchmarks/src/main/java/zipkin/benchmarks/CodecBenchmarks.java @@ -20,7 +20,6 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; import org.apache.thrift.TDeserializer; import org.apache.thrift.TException; import org.apache.thrift.TSerializer; @@ -42,9 +41,8 @@ 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.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 +156,29 @@ 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 byte[] span2Json = read("/span2.json"); + static final Span span2 = BytesDecoder.JSON.decode(span2Json); + static final List tenSpan2s = Collections.nCopies(10, span2); + static final byte[] tenSpan2sJson = BytesEncoder.JSON.encodeList(tenSpan2s); + + @Benchmark + public Span readClientSpan_json_span2() { + return BytesDecoder.JSON.decode(span2Json); + } @Benchmark public List readTenClientSpans_json_span2() { - return Decoder.JSON.decodeList(tenClientSpan2sJson); + return BytesDecoder.JSON.decodeList(tenSpan2sJson); } @Benchmark public byte[] writeClientSpan_json_span2() { - return Encoder.JSON.encode(span2); + return BytesEncoder.JSON.encode(span2); + } + + @Benchmark + public byte[] writeTenClientSpans_json_span2() { + return BytesEncoder.JSON.encodeList(tenSpan2s); } static final byte[] rpcSpanJson = read("/span-rpc.json"); @@ -246,7 +254,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..45ea81918d4 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", @@ -29,4 +29,4 @@ "http.path": "/api", "clnt/finagle.version": "6.45.0" } -}] +} 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..b252f77c993 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,7 @@ 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.BytesEncoder; import zipkin.storage.AsyncSpanConsumer; import zipkin.storage.AsyncSpanStore; import zipkin.storage.SpanStore; @@ -147,9 +146,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 = BytesEncoder.JSON.encodeList(asList( + V2SpanConverter.fromSpan(spans.get(0)).get(0), + 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..7486bf6d330 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,7 @@ 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.BytesEncoder; import zipkin.storage.AsyncSpanConsumer; import zipkin.storage.AsyncSpanStore; import zipkin.storage.SpanStore; @@ -201,9 +200,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 = BytesEncoder.JSON.encodeList(asList( + V2SpanConverter.fromSpan(spans.get(0)).get(0), + 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..e57607f31b3 100644 --- a/zipkin-junit/src/main/java/zipkin/junit/ZipkinDispatcher.java +++ b/zipkin-junit/src/main/java/zipkin/junit/ZipkinDispatcher.java @@ -13,7 +13,6 @@ */ package zipkin.junit; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.List; import javax.annotation.Nullable; @@ -32,7 +31,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; @@ -132,37 +131,17 @@ MockResponse queryV2(HttpUrl url) throws IOException { return jsonResponse(Codec.JSON.writeDependencyLinks(result)); } else if (url.encodedPath().equals("/api/v2/traces")) { List> traces = store2.getTraces(toQueryRequest2(url)).execute(); - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - bout.write('['); - for (int i = 0, length = traces.size(); i < length; ) { - List trace = traces.get(i); - writeTrace(bout, trace); - if (++i < length) bout.write(','); - } - bout.write(']'); - return jsonResponse(bout.toByteArray()); + return jsonResponse(BytesEncoder.JSON.encodeNestedList(traces)); } else if (url.encodedPath().startsWith("/api/v2/trace/")) { String traceIdHex = url.encodedPath().replace("/api/v2/trace/", ""); List trace = store2.getTrace(normalizeTraceId(traceIdHex)).execute(); if (!trace.isEmpty()) { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - writeTrace(bout, trace); - return jsonResponse(bout.toByteArray()); + return jsonResponse(BytesEncoder.JSON.encodeList(trace)); } } return new MockResponse().setResponseCode(404); } - static void writeTrace(ByteArrayOutputStream bout, List trace) - throws IOException { - bout.write('['); - for (int i = 0, length = trace.size(); i < length; ) { - bout.write(Encoder.JSON.encode(trace.get(i))); - if (++i < length) bout.write(','); - } - bout.write(']'); - } - MockResponse acceptSpans(RecordedRequest request, SpanDecoder decoder) { metrics.incrementMessages(); byte[] body = request.getBody().readByteArray(); diff --git a/zipkin-junit/src/test/java/zipkin/junit/ITHttpStorage.java b/zipkin-junit/src/test/java/zipkin/junit/ITHttpStorage.java index eeb0c354332..db7f541b4ad 100644 --- a/zipkin-junit/src/test/java/zipkin/junit/ITHttpStorage.java +++ b/zipkin-junit/src/test/java/zipkin/junit/ITHttpStorage.java @@ -15,13 +15,8 @@ import java.io.IOException; import org.junit.Rule; -import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; -import zipkin.Span; -import zipkin.TestObjects; - -import static org.assertj.core.api.Assertions.assertThat; @RunWith(Enclosed.class) public class ITHttpStorage { diff --git a/zipkin-junit/src/test/java/zipkin/junit/ZipkinRuleTest.java b/zipkin-junit/src/test/java/zipkin/junit/ZipkinRuleTest.java index 0483037e66a..a2d7e41af01 100644 --- a/zipkin-junit/src/test/java/zipkin/junit/ZipkinRuleTest.java +++ b/zipkin-junit/src/test/java/zipkin/junit/ZipkinRuleTest.java @@ -32,8 +32,7 @@ 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.BytesEncoder; import static java.lang.String.format; import static java.util.Arrays.asList; @@ -69,9 +68,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 = BytesEncoder.JSON.encodeList(asList( + V2SpanConverter.fromSpan(spans.get(0)).get(0), + 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/EnableZipkinServer.java b/zipkin-server/src/main/java/zipkin/server/EnableZipkinServer.java index 698d41133a7..7e9107e8c9c 100644 --- a/zipkin-server/src/main/java/zipkin/server/EnableZipkinServer.java +++ b/zipkin-server/src/main/java/zipkin/server/EnableZipkinServer.java @@ -19,7 +19,6 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.context.annotation.Import; -import zipkin.autoconfigure.ui.ZipkinUiAutoConfiguration; import zipkin.server.brave.BraveConfiguration; @Target(ElementType.TYPE) diff --git a/zipkin-server/src/main/java/zipkin/server/ZipkinQueryApiV2.java b/zipkin-server/src/main/java/zipkin/server/ZipkinQueryApiV2.java index 7e019eabed2..bc1c10b8f88 100644 --- a/zipkin-server/src/main/java/zipkin/server/ZipkinQueryApiV2.java +++ b/zipkin-server/src/main/java/zipkin/server/ZipkinQueryApiV2.java @@ -14,10 +14,10 @@ package zipkin.server; import java.io.IOException; +import java.nio.charset.Charset; import java.util.List; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; -import okio.Buffer; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.http.CacheControl; @@ -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; @@ -48,6 +48,8 @@ @CrossOrigin("${zipkin.query.allowed-origins:*}") @ConditionalOnProperty(name = "zipkin.query.enabled", matchIfMissing = true) public class ZipkinQueryApiV2 { + static final Charset UTF_8 = Charset.forName("UTF-8"); + final String storageType; final V2StorageComponent storage; // don't cache spanStore here as it can cause the app to crash! final long defaultLookback; @@ -126,20 +128,7 @@ public String getTraces( .limit(limit).build(); List> traces = storage.v2SpanStore().getTraces(queryRequest).execute(); - Buffer buffer = new Buffer(); - buffer.writeByte('['); - for (int i = 0, iLength = traces.size(); i < iLength; ) { - buffer.writeByte('['); - List trace = traces.get(i); - for (int j = 0, jLength = trace.size(); j < jLength; ) { - buffer.write(Encoder.JSON.encode(trace.get(j))); - if (++j < jLength) buffer.writeByte(','); - } - buffer.writeByte(']'); - if (++i < iLength) buffer.writeByte(','); - } - buffer.writeByte(']'); - return buffer.readUtf8(); + return new String(BytesEncoder.JSON.encodeNestedList(traces), UTF_8); } @RequestMapping(value = "/trace/{traceIdHex}", method = RequestMethod.GET, produces = APPLICATION_JSON_VALUE) @@ -148,14 +137,7 @@ public String getTrace(@PathVariable String traceIdHex, WebRequest request) thro List trace = storage.v2SpanStore().getTrace(traceIdHex).execute(); if (trace.isEmpty()) throw new TraceNotFoundException(traceIdHex); - Buffer buffer = new Buffer(); - buffer.writeByte('['); - for (int i = 0, length = trace.size(); i < length; ) { - buffer.write(Encoder.JSON.encode(trace.get(i))); - if (++i < length) buffer.writeByte(','); - } - buffer.writeByte(']'); - return buffer.readUtf8(); + return new String(BytesEncoder.JSON.encodeList(trace), UTF_8); } @ExceptionHandler(Version2StorageNotConfigured.class) diff --git a/zipkin-server/src/test/java/zipkin/server/ZipkinServerIntegrationTest.java b/zipkin-server/src/test/java/zipkin/server/ZipkinServerIntegrationTest.java index 24b1c356d09..7db05ec9e30 100644 --- a/zipkin-server/src/test/java/zipkin/server/ZipkinServerIntegrationTest.java +++ b/zipkin-server/src/test/java/zipkin/server/ZipkinServerIntegrationTest.java @@ -35,8 +35,7 @@ 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 static java.lang.String.format; import static java.util.Arrays.asList; @@ -86,8 +85,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 = BytesEncoder.JSON.encodeList(asList( + V2SpanConverter.fromSpan(span).get(0) )); performAsync(post("/api/v2/spans").content(message)) diff --git a/zipkin-storage/cassandra3/src/main/java/zipkin/storage/cassandra3/CassandraSpanStore.java b/zipkin-storage/cassandra3/src/main/java/zipkin/storage/cassandra3/CassandraSpanStore.java index 727eb35d1f5..56d2605cc7e 100644 --- a/zipkin-storage/cassandra3/src/main/java/zipkin/storage/cassandra3/CassandraSpanStore.java +++ b/zipkin-storage/cassandra3/src/main/java/zipkin/storage/cassandra3/CassandraSpanStore.java @@ -47,7 +47,6 @@ import java.util.Set; import java.util.concurrent.ExecutionException; import javax.annotation.Nullable; -import javax.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import zipkin.Codec; 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/HttpBulkIndexer.java b/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/HttpBulkIndexer.java index b5cdd249ccb..53b5f16a042 100644 --- a/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/HttpBulkIndexer.java +++ b/zipkin-storage/elasticsearch-http/src/main/java/zipkin/storage/elasticsearch/http/HttpBulkIndexer.java @@ -23,7 +23,6 @@ import okhttp3.RequestBody; import okio.Buffer; import zipkin.internal.JsonCodec; -import zipkin.storage.Callback; import zipkin.storage.elasticsearch.http.internal.client.HttpCall; import static zipkin.storage.elasticsearch.http.ElasticsearchHttpStorage.APPLICATION_JSON; 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..d3e8ddd64fc 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,11 @@ 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 static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; @@ -36,6 +35,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 +64,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 +74,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 +86,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 +98,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 +110,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 +122,17 @@ 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( - prefixWithTimestampMillisAndQuery(span, span.timestamp()) - )); - - assertThat(Decoder.JSON.decodeList(message)) - .containsOnly(span); // ignores timestamp_millis field + assertThat(BytesDecoder.JSON.decode(prefixWithTimestampMillisAndQuery(span, span.timestamp()))) + .isEqualTo(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 +142,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 +183,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 +193,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/InternalForTests.java b/zipkin-storage/elasticsearch-http/src/test/java/zipkin/storage/elasticsearch/http/InternalForTests.java index 580def275d0..ed6945c781d 100644 --- a/zipkin-storage/elasticsearch-http/src/test/java/zipkin/storage/elasticsearch/http/InternalForTests.java +++ b/zipkin-storage/elasticsearch-http/src/test/java/zipkin/storage/elasticsearch/http/InternalForTests.java @@ -18,7 +18,6 @@ import java.util.List; import zipkin.Codec; import zipkin.DependencyLink; -import zipkin.internal.CallbackCaptor; import static zipkin.storage.elasticsearch.http.ElasticsearchHttpSpanStore.DEPENDENCY; 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) { @Override public byte[] writeTraces(List> traces) { + return writeNestedList(SPAN_WRITER, traces); + } + + public static byte[] writeNestedList(Buffer.Writer writer, List> traces) { // Get the encoded size of the nested list so that we don't need to grow the buffer int sizeInBytes = overheadInBytes(traces); for (int i = 0, length = traces.size(); i < length; i++) { - List spans = traces.get(i); + List spans = traces.get(i); sizeInBytes += overheadInBytes(spans); for (int j = 0, jLength = spans.size(); j < jLength; j++) { - sizeInBytes += SPAN_WRITER.sizeInBytes(spans.get(j)); + sizeInBytes += writer.sizeInBytes(spans.get(j)); } } Buffer out = new Buffer(sizeInBytes); out.writeByte('['); // start list of traces for (int i = 0, length = traces.size(); i < length; i++) { - writeList(SPAN_WRITER, traces.get(i), out); + writeList(writer, traces.get(i), out); if (i + 1 < length) out.writeByte(','); } out.writeByte(']'); // stop list of traces @@ -638,7 +642,7 @@ public byte[] writeStrings(List 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)); @@ -715,7 +719,7 @@ static int overheadInBytes(List value) { return sizeInBytes; } - static byte[] writeList(Buffer.Writer writer, List value) { + public static byte[] writeList(Buffer.Writer writer, List value) { if (value.isEmpty()) return new byte[] {'[', ']'}; Buffer result = new Buffer(JsonCodec.sizeInBytes(writer, value)); writeList(writer, value, result); diff --git a/zipkin/src/main/java/zipkin/internal/Node.java b/zipkin/src/main/java/zipkin/internal/Node.java index f5dc6552839..541a1384dac 100644 --- a/zipkin/src/main/java/zipkin/internal/Node.java +++ b/zipkin/src/main/java/zipkin/internal/Node.java @@ -29,7 +29,6 @@ import static java.util.logging.Level.FINE; import static zipkin.internal.Util.checkArgument; import static zipkin.internal.Util.checkNotNull; -import static zipkin.internal.Util.toLowerHex; /** * Convenience type representing a tree. This is here because multiple facets in zipkin require 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 60% 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..d7bed047660 100644 --- a/zipkin/src/main/java/zipkin/internal/v2/codec/Encoder.java +++ b/zipkin/src/main/java/zipkin/internal/v2/codec/BytesEncoder.java @@ -13,6 +13,7 @@ */ package zipkin.internal.v2.codec; +import java.util.List; import zipkin.internal.JsonCodec; import zipkin.internal.v2.Span; @@ -21,8 +22,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; } @@ -30,14 +31,24 @@ public interface Encoder { @Override public byte[] encode(Span span) { return JsonCodec.write(SPAN_WRITER, span); } + + @Override public byte[] encodeList(List spans) { + return JsonCodec.writeList(SPAN_WRITER, spans); + } + + @Override public byte[] encodeNestedList(List> spans) { + return JsonCodec.writeNestedList(SPAN_WRITER, spans); + } }; Encoding encoding(); - /** - * Serialize a span recorded from instrumentation into its binary form. - * - * @param span cannot be null - */ + /** Serializes a span recorded from instrumentation into its binary form. */ byte[] encode(S span); + + /** Serializes a list of spans recorded from instrumentation into its binary form. */ + byte[] encodeList(List spans); + + /** Serializes a list of spans recorded from instrumentation into its binary form. */ + byte[] encodeNestedList(List> traces); } diff --git a/zipkin/src/main/java/zipkin/internal/v2/codec/MessageEncoder.java b/zipkin/src/main/java/zipkin/internal/v2/codec/MessageEncoder.java deleted file mode 100644 index 24af2fcbb09..00000000000 --- a/zipkin/src/main/java/zipkin/internal/v2/codec/MessageEncoder.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * 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.codec; - -import java.util.List; - -/** - * Senders like Kafka use byte[] message encoding. This provides helpers to concatenate spans into a - * list. - */ -public interface MessageEncoder { - MessageEncoder JSON_BYTES = new MessageEncoder() { - @Override public Encoding encoding() { - return Encoding.JSON; - } - - @Override public byte[] encode(List values) { - byte[] buf = new byte[encoding().listSizeInBytes(values)]; - int pos = 0; - buf[pos++] = '['; - for (int i = 0, length = values.size(); i < length; ) { - byte[] v = values.get(i++); - System.arraycopy(v, 0, buf, pos, v.length); - pos += v.length; - if (i < length) buf[pos++] = ','; - } - buf[pos] = ']'; - return buf; - } - }; - - Encoding encoding(); - - /** - * Combines a list of encoded spans into an encoded list. For example, in json, this would be - * 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 - * 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/main/java/zipkin/internal/v2/storage/SpanConsumer.java b/zipkin/src/main/java/zipkin/internal/v2/storage/SpanConsumer.java index 789b245292e..a197ada8bab 100644 --- a/zipkin/src/main/java/zipkin/internal/v2/storage/SpanConsumer.java +++ b/zipkin/src/main/java/zipkin/internal/v2/storage/SpanConsumer.java @@ -16,7 +16,6 @@ import java.util.List; import zipkin.internal.v2.Call; import zipkin.internal.v2.Span; -import zipkin.storage.Callback; // @FunctionalInterface public interface SpanConsumer { diff --git a/zipkin/src/main/java/zipkin/storage/AsyncSpanConsumer.java b/zipkin/src/main/java/zipkin/storage/AsyncSpanConsumer.java index dfb084c1fbf..2b96df71a5d 100644 --- a/zipkin/src/main/java/zipkin/storage/AsyncSpanConsumer.java +++ b/zipkin/src/main/java/zipkin/storage/AsyncSpanConsumer.java @@ -16,7 +16,6 @@ import java.util.List; import zipkin.Codec; import zipkin.Span; -import zipkin.collector.CollectorSampler; /** * Spans are created in instrumentation, transported out-of-band, and eventually persisted. A span diff --git a/zipkin/src/test/java/zipkin/collector/CollectorTest.java b/zipkin/src/test/java/zipkin/collector/CollectorTest.java index 52e396e352f..75021fe57bd 100644 --- a/zipkin/src/test/java/zipkin/collector/CollectorTest.java +++ b/zipkin/src/test/java/zipkin/collector/CollectorTest.java @@ -22,8 +22,7 @@ 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.storage.SpanConsumer; import static java.util.Arrays.asList; @@ -85,7 +84,7 @@ public class CollectorTest { } @Test public void convertsSpan2Format() { - byte[] bytes = MessageEncoder.JSON_BYTES.encode(asList(Encoder.JSON.encode(span2_1))); + byte[] bytes = BytesEncoder.JSON.encodeList(asList(span2_1)); collector.acceptSpans(bytes, SpanDecoder.DETECTING_DECODER, NOOP); verify(collector).acceptSpans(bytes, SpanDecoder.DETECTING_DECODER, NOOP); @@ -107,7 +106,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 = BytesEncoder.JSON.encodeList(asList(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..d247f252bfd 100644 --- a/zipkin/src/test/java/zipkin/internal/DetectingSpanDecoderTest.java +++ b/zipkin/src/test/java/zipkin/internal/DetectingSpanDecoderTest.java @@ -13,14 +13,11 @@ */ package zipkin.internal; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.junit.Test; 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 static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; @@ -72,17 +69,15 @@ 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 = BytesEncoder.JSON.encodeList(asList(span2_1, span2_2)); assertThat(decoder.readSpans(message)) .containsExactly(span1, span2); diff --git a/zipkin/src/test/java/zipkin/internal/V2JsonSpanDecoderTest.java b/zipkin/src/test/java/zipkin/internal/V2JsonSpanDecoderTest.java index af7114fcfc5..5cf9c5aba6f 100644 --- a/zipkin/src/test/java/zipkin/internal/V2JsonSpanDecoderTest.java +++ b/zipkin/src/test/java/zipkin/internal/V2JsonSpanDecoderTest.java @@ -16,8 +16,7 @@ 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 static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; @@ -32,14 +31,11 @@ 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 = BytesEncoder.JSON.encodeList(asList(span2_1, span2_2)); assertThat(decoder.readSpans(message)) .containsExactly(span1, span2); 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/MessageEncodingTest.java deleted file mode 100644 index 151c6521045..00000000000 --- a/zipkin/src/test/java/zipkin/internal/v2/codec/MessageEncodingTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * 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.codec; - -import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import org.junit.Test; -import zipkin.internal.Util; - -import static org.assertj.core.api.Assertions.assertThat; - -public class MessageEncodingTest { - - @Test public void emptyList_jsonBytes() throws IOException { - List encoded = Arrays.asList(); - assertThat(MessageEncoder.JSON_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)) - .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)) - .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..31aef0a3c07 100644 --- a/zipkin/src/test/java/zipkin/internal/v2/codec/SpanJsonAdaptersTest.java +++ b/zipkin/src/test/java/zipkin/internal/v2/codec/SpanJsonAdaptersTest.java @@ -14,21 +14,20 @@ package zipkin.internal.v2.codec; import java.io.IOException; -import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import zipkin.Constants; import zipkin.Endpoint; import zipkin.TraceKeys; -import zipkin.internal.v2.Span; import zipkin.internal.Util; +import zipkin.internal.v2.Span; 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 +37,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,34 +56,34 @@ public class SpanJsonAdaptersTest { @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void spanRoundTrip() throws IOException { - assertThat(Decoder.JSON.decodeList(encodeList(span))) - .containsOnly(span); + assertThat(BytesDecoder.JSON.decode(BytesEncoder.JSON.encode(span))) + .isEqualTo(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))) - .containsOnly(span); + assertThat(BytesDecoder.JSON.decode(BytesEncoder.JSON.encode(span))) + .isEqualTo(span); } @Test public void spanRoundTrip_shared() throws IOException { span = span.toBuilder().shared(true).build(); - assertThat(Decoder.JSON.decodeList(encodeList(span))) - .containsOnly(span); + assertThat(BytesDecoder.JSON.decode(BytesEncoder.JSON.encode(span))) + .isEqualTo(span); } @Test public void sizeInBytes_64bitTraceId() throws IOException { 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,38 +92,37 @@ 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))) - .containsOnly(worstSpanInTheWorld); + assertThat(BytesDecoder.JSON.decode(BytesEncoder.JSON.encode(worstSpanInTheWorld))) + .isEqualTo(worstSpanInTheWorld); } @Test public void niceErrorOnUppercase_traceId() { thrown.expect(IllegalArgumentException.class); thrown.expectMessage("48485A3953BB6124 should be lower-hex encoded with no prefix"); - String json = "[{\n" + String json = "{\n" + " \"traceId\": \"48485A3953BB6124\",\n" + " \"name\": \"get-traces\",\n" + " \"id\": \"6b221d5bc9e6496c\"\n" - + "}]"; + + "}"; - Decoder.JSON.decodeList(json.getBytes(UTF_8)); + BytesDecoder.JSON.decode(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,49 +132,47 @@ 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 = BytesEncoder.JSON.encodeList(tenClientSpans); - 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\""); } @Test public void readsTraceIdHighFromTraceIdField() { - byte[] with128BitTraceId = ("[{\n" + byte[] with128BitTraceId = ("{\n" + " \"traceId\": \"48485a3953bb61246b221d5bc9e6496c\",\n" + " \"name\": \"get-traces\",\n" + " \"id\": \"6b221d5bc9e6496c\"\n" - + "}]").getBytes(UTF_8); - byte[] withLower64bitsTraceId = ("[{\n" + + "}").getBytes(UTF_8); + byte[] withLower64bitsTraceId = ("{\n" + " \"traceId\": \"6b221d5bc9e6496c\",\n" + " \"name\": \"get-traces\",\n" + " \"id\": \"6b221d5bc9e6496c\"\n" - + "}]").getBytes(UTF_8); + + "}").getBytes(UTF_8); - assertThat(Decoder.JSON.decodeList(with128BitTraceId).get(0)) - .isEqualTo(Decoder.JSON.decodeList(withLower64bitsTraceId).get(0).toBuilder() + assertThat(BytesDecoder.JSON.decode(with128BitTraceId)) + .isEqualTo(BytesDecoder.JSON.decode(withLower64bitsTraceId).toBuilder() .traceId("48485a3953bb61246b221d5bc9e6496c").build()); } @Test public void ignoresNull_topLevelFields() { - String json = "[{\n" + String json = "{\n" + " \"traceId\": \"6b221d5bc9e6496c\",\n" + " \"parentId\": null,\n" + " \"id\": \"6b221d5bc9e6496c\",\n" @@ -189,13 +185,13 @@ public class SpanJsonAdaptersTest { + " \"tags\": null,\n" + " \"debug\": null,\n" + " \"shared\": null\n" - + "}]"; + + "}"; - Decoder.JSON.decodeList(json.getBytes(UTF_8)); + BytesDecoder.JSON.decode(json.getBytes(UTF_8)); } @Test public void ignoresNull_endpoint_topLevelFields() { - String json = "[{\n" + String json = "{\n" + " \"traceId\": \"6b221d5bc9e6496c\",\n" + " \"name\": \"get-traces\",\n" + " \"id\": \"6b221d5bc9e6496c\",\n" @@ -205,17 +201,17 @@ public class SpanJsonAdaptersTest { + " \"ipv6\": null,\n" + " \"port\": null\n" + " }\n" - + "}]"; + + "}"; - assertThat(Decoder.JSON.decodeList(json.getBytes(UTF_8)).get(0).localEndpoint()) + assertThat(convert(BytesDecoder.JSON.decode(json.getBytes(UTF_8)).localEndpoint())) .isEqualTo(Endpoint.create("", 127 << 24 | 1)); } @Test public void niceErrorOnIncomplete_endpoint() { thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("Empty endpoint at $[0].localEndpoint reading List from json"); + thrown.expectMessage("Empty endpoint at $.localEndpoint reading Span from json"); - String json = "[{\n" + String json = "{\n" + " \"traceId\": \"6b221d5bc9e6496c\",\n" + " \"name\": \"get-traces\",\n" + " \"id\": \"6b221d5bc9e6496c\",\n" @@ -225,147 +221,142 @@ public class SpanJsonAdaptersTest { + " \"ipv6\": null,\n" + " \"port\": null\n" + " }\n" - + "}]"; - Decoder.JSON.decodeList(json.getBytes(UTF_8)); + + "}"; + BytesDecoder.JSON.decode(json.getBytes(UTF_8)); } @Test public void niceErrorOnIncomplete_annotation() { thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("Incomplete annotation at $[0].annotations[0].timestamp"); + thrown.expectMessage("Incomplete annotation at $.annotations[0].timestamp"); - String json = "[{\n" + String json = "{\n" + " \"traceId\": \"6b221d5bc9e6496c\",\n" + " \"name\": \"get-traces\",\n" + " \"id\": \"6b221d5bc9e6496c\",\n" + " \"annotations\": [\n" + " { \"timestamp\": 1472470996199000}\n" + " ]\n" - + "}]"; + + "}"; - Decoder.JSON.decodeList(json.getBytes(UTF_8)); + BytesDecoder.JSON.decode(json.getBytes(UTF_8)); } @Test public void niceErrorOnNull_traceId() { thrown.expect(IllegalArgumentException.class); thrown.expectMessage("Expected a string but was NULL"); - String json = "[{\n" + String json = "{\n" + " \"traceId\": null,\n" + " \"name\": \"get-traces\",\n" + " \"id\": \"6b221d5bc9e6496c\"\n" - + "}]"; + + "}"; - Decoder.JSON.decodeList(json.getBytes(UTF_8)); + BytesDecoder.JSON.decode(json.getBytes(UTF_8)); } @Test public void niceErrorOnNull_id() { thrown.expect(IllegalArgumentException.class); thrown.expectMessage("Expected a string but was NULL"); - String json = "[{\n" + String json = "{\n" + " \"traceId\": \"6b221d5bc9e6496c\",\n" + " \"name\": \"get-traces\",\n" + " \"id\": null\n" - + "}]"; + + "}"; - Decoder.JSON.decodeList(json.getBytes(UTF_8)); + BytesDecoder.JSON.decode(json.getBytes(UTF_8)); } @Test public void niceErrorOnNull_tagValue() { thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("No value at $[0].tags.foo"); + thrown.expectMessage("No value at $.tags.foo"); - String json = "[{\n" + String json = "{\n" + " \"traceId\": \"6b221d5bc9e6496c\",\n" + " \"name\": \"get-traces\",\n" + " \"id\": \"6b221d5bc9e6496c\",\n" + " \"tags\": {\n" + " \"foo\": NULL\n" + " }\n" - + "}]"; + + "}"; - Decoder.JSON.decodeList(json.getBytes(UTF_8)); + BytesDecoder.JSON.decode(json.getBytes(UTF_8)); } @Test public void niceErrorOnNull_annotationValue() { thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("$[0].annotations[0].value"); + thrown.expectMessage("$.annotations[0].value"); - String json = "[{\n" + String json = "{\n" + " \"traceId\": \"6b221d5bc9e6496c\",\n" + " \"name\": \"get-traces\",\n" + " \"id\": \"6b221d5bc9e6496c\",\n" + " \"annotations\": [\n" + " { \"timestamp\": 1472470996199000, \"value\": NULL}\n" + " ]\n" - + "}]"; + + "}"; - Decoder.JSON.decodeList(json.getBytes(UTF_8)); + BytesDecoder.JSON.decode(json.getBytes(UTF_8)); } @Test public void niceErrorOnNull_annotationTimestamp() { thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("$[0].annotations[0].timestamp"); + thrown.expectMessage("$.annotations[0].timestamp"); - String json = "[{\n" + String json = "{\n" + " \"traceId\": \"6b221d5bc9e6496c\",\n" + " \"name\": \"get-traces\",\n" + " \"id\": \"6b221d5bc9e6496c\",\n" + " \"annotations\": [\n" + " { \"timestamp\": NULL, \"value\": \"foo\"}\n" + " ]\n" - + "}]"; + + "}"; - Decoder.JSON.decodeList(json.getBytes(UTF_8)); + BytesDecoder.JSON.decode(json.getBytes(UTF_8)); } @Test public void readSpan_localEndpoint_noServiceName() { - String json = "[{\n" + String json = "{\n" + " \"traceId\": \"6b221d5bc9e6496c\",\n" + " \"name\": \"get-traces\",\n" + " \"id\": \"6b221d5bc9e6496c\",\n" + " \"localEndpoint\": {\n" + " \"ipv4\": \"127.0.0.1\"\n" + " }\n" - + "}]"; + + "}"; - assertThat(Decoder.JSON.decodeList(json.getBytes(UTF_8)).get(0).localEndpoint()) - .isEqualTo(Endpoint.create("", 127 << 24 | 1)); + assertThat(BytesDecoder.JSON.decode(json.getBytes(UTF_8)).localServiceName()) + .isNull(); } @Test public void readSpan_remoteEndpoint_noServiceName() { - String json = "[{\n" + String json = "{\n" + " \"traceId\": \"6b221d5bc9e6496c\",\n" + " \"name\": \"get-traces\",\n" + " \"id\": \"6b221d5bc9e6496c\",\n" + " \"remoteEndpoint\": {\n" + " \"ipv4\": \"127.0.0.1\"\n" + " }\n" - + "}]"; + + "}"; - assertThat(Decoder.JSON.decodeList(json.getBytes(UTF_8)).get(0).remoteEndpoint()) - .isEqualTo(Endpoint.create("", 127 << 24 | 1)); + assertThat(BytesDecoder.JSON.decode(json.getBytes(UTF_8)).remoteServiceName()) + .isNull(); } @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))) - .containsOnly(span); + assertThat(BytesDecoder.JSON.decode(BytesEncoder.JSON.encode(span))) + .isEqualTo(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()) - ); - } } 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();