Repository: incubator-htrace Updated Branches: refs/heads/master bc63ac8cc -> 6b36b1aa6
HTRACE-31. more JSON serialization fixes (cmccabe) Project: http://git-wip-us.apache.org/repos/asf/incubator-htrace/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-htrace/commit/6b36b1aa Tree: http://git-wip-us.apache.org/repos/asf/incubator-htrace/tree/6b36b1aa Diff: http://git-wip-us.apache.org/repos/asf/incubator-htrace/diff/6b36b1aa Branch: refs/heads/master Commit: 6b36b1aa677d1242ac850fdb05b6016842e881e3 Parents: bc63ac8 Author: Colin P. Mccabe <[email protected]> Authored: Tue Dec 30 14:06:38 2014 -0800 Committer: Colin P. Mccabe <[email protected]> Committed: Tue Dec 30 14:07:43 2014 -0800 ---------------------------------------------------------------------- htrace-core/pom.xml | 15 +- .../src/go/src/org/apache/htrace/common/span.go | 19 +- .../src/org/apache/htrace/common/span_test.go | 4 +- .../src/go/src/org/apache/htrace/htrace/cmd.go | 4 +- .../go/src/org/apache/htrace/htraced/rest.go | 6 +- .../src/main/java/org/apache/htrace/Span.java | 48 +++++ .../java/org/apache/htrace/impl/MilliSpan.java | 206 +++++++++++++++---- .../org/apache/htrace/impl/TestMilliSpan.java | 120 +++++++++++ 8 files changed, 368 insertions(+), 54 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/6b36b1aa/htrace-core/pom.xml ---------------------------------------------------------------------- diff --git a/htrace-core/pom.xml b/htrace-core/pom.xml index 8ce720d..54c6c6c 100644 --- a/htrace-core/pom.xml +++ b/htrace-core/pom.xml @@ -61,8 +61,8 @@ language governing permissions and limitations under the License. --> <shadedPattern>org.apache.htrace.commons.logging</shadedPattern> </relocation> <relocation> - <pattern>org.mortbay</pattern> - <shadedPattern>org.apache.htrace.mortbay</shadedPattern> + <pattern>com.fasterxml.jackson.core</pattern> + <shadedPattern>org.apache.htrace.fasterxml.jackson.core</shadedPattern> </relocation> </relocations> </configuration> @@ -137,9 +137,14 @@ language governing permissions and limitations under the License. --> <artifactId>commons-logging</artifactId> </dependency> <dependency> - <groupId>org.mortbay.jetty</groupId> - <artifactId>jetty-util</artifactId> - <version>6.1.26</version> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-core</artifactId> + <version>2.4.0</version> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + <version>2.4.0</version> </dependency> </dependencies> </project> http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/6b36b1aa/htrace-core/src/go/src/org/apache/htrace/common/span.go ---------------------------------------------------------------------- diff --git a/htrace-core/src/go/src/org/apache/htrace/common/span.go b/htrace-core/src/go/src/org/apache/htrace/common/span.go index 81fe0e8..540ba12 100644 --- a/htrace-core/src/go/src/org/apache/htrace/common/span.go +++ b/htrace-core/src/go/src/org/apache/htrace/common/span.go @@ -38,14 +38,14 @@ import ( type TraceInfoMap map[string][]byte type TimelineAnnotation struct { - Time int64 `json:"time,string"` - Msg string `json:"msg"` + Time int64 `json:"t"` + Msg string `json:"m"` } type SpanId int64 func (id SpanId) String() string { - return fmt.Sprintf("%08x", id) + return fmt.Sprintf("%016x", id) } func (id SpanId) Val() int64 { @@ -56,13 +56,18 @@ func (id SpanId) MarshalJSON() ([]byte, error) { return []byte(`"` + fmt.Sprintf("%016x", uint64(id)) + `"`), nil } -func (id SpanId) UnMarshalJSON() ([]byte, error) { - return []byte(`"` + strconv.FormatUint(uint64(id), 16) + `"`), nil +func (id *SpanId) UnMarshalJSON(b []byte) error { + v, err := strconv.ParseUint(string(b), 16, 64) + if err != nil { + return err + } + *id = SpanId(v) + return nil } type SpanData struct { - Begin int64 `json:"b,string"` - End int64 `json:"e,string"` + Begin int64 `json:"b"` + End int64 `json:"e"` Description string `json:"d"` TraceId SpanId `json:"i"` Parents []SpanId `json:"p"` http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/6b36b1aa/htrace-core/src/go/src/org/apache/htrace/common/span_test.go ---------------------------------------------------------------------- diff --git a/htrace-core/src/go/src/org/apache/htrace/common/span_test.go b/htrace-core/src/go/src/org/apache/htrace/common/span_test.go index 1d098fc..f218b3a 100644 --- a/htrace-core/src/go/src/org/apache/htrace/common/span_test.go +++ b/htrace-core/src/go/src/org/apache/htrace/common/span_test.go @@ -35,7 +35,7 @@ func TestSpanToJson(t *testing.T) { ProcessId: "testProcessId", }} ExpectStrEqual(t, - `{"s":"2000000000000000","b":"123","e":"456","d":"getFileDescriptors","i":"00000000000003e7","p":[],"r":"testProcessId"}`, + `{"s":"2000000000000000","b":123,"e":456,"d":"getFileDescriptors","i":"00000000000003e7","p":[],"r":"testProcessId"}`, string(span.ToJson())) } @@ -61,6 +61,6 @@ func TestAnnotatedSpanToJson(t *testing.T) { }, }} ExpectStrEqual(t, - `{"s":"121f2e036d442000","b":"1234","e":"4567","d":"getFileDescriptors2","i":"00000000000003e7","p":[],"r":"testAnnotatedProcessId","t":[{"time":"7777","msg":"contactedServer"},{"time":"8888","msg":"passedFd"}]}`, + `{"s":"121f2e036d442000","b":1234,"e":4567,"d":"getFileDescriptors2","i":"00000000000003e7","p":[],"r":"testAnnotatedProcessId","t":[{"t":7777,"m":"contactedServer"},{"t":8888,"m":"passedFd"}]}`, string(span.ToJson())) } http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/6b36b1aa/htrace-core/src/go/src/org/apache/htrace/htrace/cmd.go ---------------------------------------------------------------------- diff --git a/htrace-core/src/go/src/org/apache/htrace/htrace/cmd.go b/htrace-core/src/go/src/org/apache/htrace/htrace/cmd.go index d4bb253..9633ba3 100644 --- a/htrace-core/src/go/src/org/apache/htrace/htrace/cmd.go +++ b/htrace-core/src/go/src/org/apache/htrace/htrace/cmd.go @@ -90,7 +90,7 @@ func printServerInfo(restAddr string) int { // Print information about a trace span. func doFindSpan(restAddr string, sid int64) int { - buf, err := makeRestRequest(restAddr, fmt.Sprintf("findSid?sid=%d", sid)) + buf, err := makeRestRequest(restAddr, fmt.Sprintf("findSid?sid=%016x", sid)) if err != nil { fmt.Printf("%s\n", err.Error()) return 1 @@ -113,7 +113,7 @@ func doFindSpan(restAddr string, sid int64) int { // Find information about the children of a span. func doFindChildren(restAddr string, sid int64, lim int) int { - buf, err := makeRestRequest(restAddr, fmt.Sprintf("findChildren?sid=%d&lim=%d", sid, lim)) + buf, err := makeRestRequest(restAddr, fmt.Sprintf("findChildren?sid=%016x&lim=%d", sid, lim)) if err != nil { fmt.Printf("%s\n", err.Error()) return 1 http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/6b36b1aa/htrace-core/src/go/src/org/apache/htrace/htraced/rest.go ---------------------------------------------------------------------- diff --git a/htrace-core/src/go/src/org/apache/htrace/htraced/rest.go b/htrace-core/src/go/src/org/apache/htrace/htraced/rest.go index f0bb2c1..5b97313 100644 --- a/htrace-core/src/go/src/org/apache/htrace/htraced/rest.go +++ b/htrace-core/src/go/src/org/apache/htrace/htraced/rest.go @@ -55,13 +55,13 @@ func (hand *dataStoreHandler) getReqField64(fieldName string, w http.ResponseWri w.Write([]byte("No " + fieldName + " specified.")) return -1, false } - val, err := strconv.ParseInt(str, 10, 64) + val, err := strconv.ParseUint(str, 16, 64) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Error parsing " + fieldName + ": " + err.Error())) return -1, false } - return val, true + return int64(val), true } func (hand *dataStoreHandler) getReqField32(fieldName string, w http.ResponseWriter, @@ -72,7 +72,7 @@ func (hand *dataStoreHandler) getReqField32(fieldName string, w http.ResponseWri w.Write([]byte("No " + fieldName + " specified.")) return -1, false } - val, err := strconv.ParseInt(str, 10, 32) + val, err := strconv.ParseUint(str, 16, 32) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Error parsing " + fieldName + ": " + err.Error())) http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/6b36b1aa/htrace-core/src/main/java/org/apache/htrace/Span.java ---------------------------------------------------------------------- diff --git a/htrace-core/src/main/java/org/apache/htrace/Span.java b/htrace-core/src/main/java/org/apache/htrace/Span.java index 50cec7c..b8af10d 100644 --- a/htrace-core/src/main/java/org/apache/htrace/Span.java +++ b/htrace-core/src/main/java/org/apache/htrace/Span.java @@ -16,6 +16,12 @@ */ package org.apache.htrace; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import java.io.IOException; import java.util.List; import java.util.Map; @@ -27,6 +33,7 @@ import java.util.Map; * Spans form a tree structure with the parent relationship. The first span in a * trace has no parent span. */ +@JsonSerialize(using = Span.SpanSerializer.class) public interface Span { public static final long ROOT_SPAN_ID = 0x74ace; @@ -118,4 +125,45 @@ public interface Span { * Serialize to Json */ String toJson(); + + public static class SpanSerializer extends JsonSerializer<Span> { + @Override + public void serialize(Span span, JsonGenerator jgen, SerializerProvider provider) + throws IOException { + jgen.writeStartObject(); + jgen.writeStringField("i", String.format("%016x", span.getTraceId())); + jgen.writeStringField("s", String.format("%016x", span.getSpanId())); + jgen.writeNumberField("b", span.getStartTimeMillis()); + jgen.writeNumberField("e", span.getStopTimeMillis()); + jgen.writeStringField("d", span.getDescription()); + jgen.writeStringField("r", span.getProcessId()); + jgen.writeArrayFieldStart("p"); + if (span.getParentId() != ROOT_SPAN_ID) { + jgen.writeString(String.format("%016x", span.getParentId())); + } + jgen.writeEndArray(); + Map<byte[], byte[]> traceInfoMap = span.getKVAnnotations(); + if (!traceInfoMap.isEmpty()) { + jgen.writeObjectFieldStart("n"); + for (Map.Entry<byte[], byte[]> e : traceInfoMap.entrySet()) { + jgen.writeStringField(new String(e.getKey(), "UTF-8"), + new String(e.getValue(), "UTF-8")); + } + jgen.writeEndObject(); + } + List<TimelineAnnotation> timelineAnnotations = + span.getTimelineAnnotations(); + if (!timelineAnnotations.isEmpty()) { + jgen.writeArrayFieldStart("t"); + for (TimelineAnnotation tl : timelineAnnotations) { + jgen.writeStartObject(); + jgen.writeNumberField("t", tl.getTime()); + jgen.writeStringField("m", tl.getMessage()); + jgen.writeEndObject(); + } + jgen.writeEndArray(); + } + jgen.writeEndObject(); + } + } } http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/6b36b1aa/htrace-core/src/main/java/org/apache/htrace/impl/MilliSpan.java ---------------------------------------------------------------------- diff --git a/htrace-core/src/main/java/org/apache/htrace/impl/MilliSpan.java b/htrace-core/src/main/java/org/apache/htrace/impl/MilliSpan.java index b58839b..3932b79 100644 --- a/htrace-core/src/main/java/org/apache/htrace/impl/MilliSpan.java +++ b/htrace-core/src/main/java/org/apache/htrace/impl/MilliSpan.java @@ -16,15 +16,24 @@ */ package org.apache.htrace.impl; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import org.apache.htrace.Span; import org.apache.htrace.TimelineAnnotation; import org.apache.htrace.Tracer; -import org.mortbay.util.ajax.JSON; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.LinkedHashMap; +import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; @@ -33,12 +42,13 @@ import java.util.Random; * A Span implementation that stores its information in milliseconds since the * epoch. */ +@JsonDeserialize(using = MilliSpan.MilliSpanDeserializer.class) public class MilliSpan implements Span { private static Random rand = new Random(); - private long start; - private long stop; + private long begin; + private long end; private final String description; private final long traceId; private final long parents[]; @@ -52,6 +62,94 @@ public class MilliSpan implements Span { return new MilliSpan(description, traceId, spanId, rand.nextLong(), processId); } + /** + * The public interface for constructing a MilliSpan. + */ + public static class Builder { + private long begin; + private long end; + private String description; + private long traceId; + private long parents[]; + private long spanId; + private Map<byte[], byte[]> traceInfo = null; + private String processId; + private List<TimelineAnnotation> timeline = null; + + public Builder() { + } + + public Builder begin(long begin) { + this.begin = begin; + return this; + } + + public Builder end(long end) { + this.end = end; + return this; + } + + public Builder description(String description) { + this.description = description; + return this; + } + + public Builder traceId(long traceId) { + this.traceId = traceId; + return this; + } + + public Builder parents(long parents[]) { + this.parents = parents; + return this; + } + + public Builder parents(List<Long> parentList) { + long[] parents = new long[parentList.size()]; + for (int i = 0; i < parentList.size(); i++) { + parents[i] = parentList.get(i).longValue(); + } + this.parents = parents; + return this; + } + + public Builder spanId(long spanId) { + this.spanId = spanId; + return this; + } + + public Builder traceInfo(Map<byte[], byte[]> traceInfo) { + this.traceInfo = traceInfo.isEmpty() ? null : traceInfo; + return this; + } + + public Builder processId(String processId) { + this.processId = processId; + return this; + } + + public Builder timeline(List<TimelineAnnotation> timeline) { + this.timeline = timeline.isEmpty() ? null : timeline; + return this; + } + + public MilliSpan build() { + return new MilliSpan(this); + } + } + + private MilliSpan(Builder builder) { + this.begin = builder.begin; + this.end = builder.end; + this.description = builder.description; + this.traceId = builder.traceId; + this.parents = builder.parents; + this.spanId = builder.spanId; + this.traceInfo = builder.traceInfo; + this.processId = builder.processId; + this.timeline = builder.timeline; + } + public MilliSpan(String description, long traceId, long parentSpanId, long spanId, String processId) { this.description = description; this.traceId = traceId; @@ -61,18 +159,18 @@ public class MilliSpan implements Span { this.parents = new long[] { parentSpanId }; } this.spanId = spanId; - this.start = System.currentTimeMillis(); - this.stop = 0; + this.begin = System.currentTimeMillis(); + this.end = 0; this.processId = processId; } @Override public synchronized void stop() { - if (stop == 0) { - if (start == 0) + if (end == 0) { + if (begin == 0) throw new IllegalStateException("Span for " + description + " has not been started"); - stop = System.currentTimeMillis(); + end = System.currentTimeMillis(); Tracer.getInstance().deliver(this); } } @@ -83,16 +181,16 @@ public class MilliSpan implements Span { @Override public synchronized boolean isRunning() { - return start != 0 && stop == 0; + return begin != 0 && end == 0; } @Override public synchronized long getAccumulatedMillis() { - if (start == 0) + if (begin == 0) return 0; - if (stop > 0) - return stop - start; - return currentTimeMillis() - start; + if (end > 0) + return end - begin; + return currentTimeMillis() - begin; } @Override @@ -127,12 +225,12 @@ public class MilliSpan implements Span { @Override public long getStartTimeMillis() { - return start; + return begin; } @Override public long getStopTimeMillis() { - return stop; + return end; } @Override @@ -172,24 +270,62 @@ public class MilliSpan implements Span { @Override public String toJson() { - Map<String, Object> values = new LinkedHashMap<String, Object>(); - values.put("i", String.format("%016x", traceId)); - values.put("s", String.format("%016x", spanId)); - String parentStrs[] = new String[parents.length]; - for (int parentIdx = 0; parentIdx < parents.length; parentIdx++) { - parentStrs[parentIdx] = String.format("%016x", parents[parentIdx]); - } - values.put("p", parentStrs); - values.put("r", processId); - values.put("b", Long.toString(start)); - values.put("e", Long.toString(stop)); - values.put("d", description); - if (timeline != null) { - values.put("t", timeline); - } - if (traceInfo != null){ - values.put("n", traceInfo); - } - return JSON.toString(values); + StringWriter writer = new StringWriter(); + ObjectMapper mapper = new ObjectMapper(); + try { + mapper.writeValue(writer, this); + } catch (IOException e) { + // An IOException should not be possible when writing to a string. + throw new RuntimeException(e); + } + return writer.toString(); + } + + public static class MilliSpanDeserializer + extends JsonDeserializer<MilliSpan> { + @Override + public MilliSpan deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + JsonNode root = jp.getCodec().readTree(jp); + Builder builder = new Builder(); + builder.begin(root.get("b").asLong()). + end(root.get("e").asLong()). + description(root.get("d").asText()). + traceId(Long.parseLong(root.get("i").asText(), 16)). + spanId(Long.parseLong(root.get("s").asText(), 16)). + processId(root.get("r").asText()); + JsonNode parentsNode = root.get("p"); + LinkedList<Long> parents = new LinkedList<Long>(); + for (Iterator<JsonNode> iter = parentsNode.elements(); + iter.hasNext(); ) { + JsonNode parentIdNode = iter.next(); + parents.add(Long.parseLong(parentIdNode.asText(), 16)); + } + builder.parents(parents); + JsonNode traceInfoNode = root.get("n"); + if (traceInfoNode != null) { + HashMap<byte[], byte[]> traceInfo = new HashMap<byte[], byte[]>(); + for (Iterator<String> iter = traceInfoNode.fieldNames(); + iter.hasNext(); ) { + String field = iter.next(); + traceInfo.put(field.getBytes("UTF-8"), + traceInfoNode.get(field).asText().getBytes("UTF-8")); + } + builder.traceInfo(traceInfo); + } + JsonNode timelineNode = root.get("t"); + if (timelineNode != null) { + LinkedList<TimelineAnnotation> timeline = + new LinkedList<TimelineAnnotation>(); + for (Iterator<JsonNode> iter = timelineNode.elements(); + iter.hasNext(); ) { + JsonNode ann = iter.next(); + timeline.add(new TimelineAnnotation(ann.get("t").asLong(), + ann.get("m").asText())); + } + builder.timeline(timeline); + } + return builder.build(); + } } } http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/6b36b1aa/htrace-core/src/test/java/org/apache/htrace/impl/TestMilliSpan.java ---------------------------------------------------------------------- diff --git a/htrace-core/src/test/java/org/apache/htrace/impl/TestMilliSpan.java b/htrace-core/src/test/java/org/apache/htrace/impl/TestMilliSpan.java new file mode 100644 index 0000000..908e74e --- /dev/null +++ b/htrace-core/src/test/java/org/apache/htrace/impl/TestMilliSpan.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.htrace.impl; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import static org.junit.Assert.assertEquals; + +import org.apache.htrace.Span; +import org.apache.htrace.TimelineAnnotation; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class TestMilliSpan { + private void compareSpans(Span expected, Span got) throws Exception { + assertEquals(expected.getStartTimeMillis(), got.getStartTimeMillis()); + assertEquals(expected.getStopTimeMillis(), got.getStopTimeMillis()); + assertEquals(expected.getDescription(), got.getDescription()); + assertEquals(expected.getTraceId(), got.getTraceId()); + assertEquals(expected.getSpanId(), got.getSpanId()); + assertEquals(expected.getProcessId(), got.getProcessId()); + assertEquals(expected.getParentId(), got.getParentId()); + Map<byte[], byte[]> expectedT = expected.getKVAnnotations(); + Map<byte[], byte[]> gotT = got.getKVAnnotations(); + if (expectedT == null) { + assertEquals(null, gotT); + } else { + assertEquals(expectedT.size(), gotT.size()); + Map<String, String> expectedTMap = new HashMap<String, String>(); + for (byte[] key : expectedT.keySet()) { + expectedTMap.put(new String(key, "UTF-8"), + new String(expectedT.get(key), "UTF-8")); + } + Map<String, String> gotTMap = new HashMap<String, String>(); + for (byte[] key : gotT.keySet()) { + gotTMap.put(new String(key, "UTF-8"), + new String(gotT.get(key), "UTF-8")); + } + for (String key : expectedTMap.keySet()) { + assertEquals(expectedTMap.get(key), gotTMap.get(key)); + } + } + List<TimelineAnnotation> expectedTimeline = + expected.getTimelineAnnotations(); + List<TimelineAnnotation> gotTimeline = + got.getTimelineAnnotations(); + if (expectedTimeline == null) { + assertEquals(null, gotTimeline); + } else { + assertEquals(expectedTimeline.size(), gotTimeline.size()); + Iterator<TimelineAnnotation> iter = gotTimeline.iterator(); + for (TimelineAnnotation expectedAnn : expectedTimeline) { + TimelineAnnotation gotAnn = iter.next(); + assertEquals(expectedAnn.getMessage(), gotAnn.getMessage()); + assertEquals(expectedAnn.getTime(), gotAnn.getTime()); + } + } + } + + @Test + public void testJsonSerialization() throws Exception { + MilliSpan span = new MilliSpan.Builder(). + description("foospan"). + begin(123L). + end(456L). + parents(new long[] { 7L }). + processId("b2404.halxg.com:8080"). + spanId(989L). + traceId(444).build(); + String json = span.toJson(); + ObjectMapper mapper = new ObjectMapper(); + MilliSpan dspan = mapper.readValue(json, MilliSpan.class); + compareSpans(span, dspan); + } + + @Test + public void testJsonSerializationWithOptionalFields() throws Exception { + MilliSpan.Builder builder = new MilliSpan.Builder(). + description("foospan"). + begin(300). + end(400). + parents(new long[] { }). + processId("b2408.halxg.com:8080"). + spanId(111111111L). + traceId(4443); + Map<byte[], byte[]> traceInfo = new HashMap<byte[], byte[]>(); + traceInfo.put("abc".getBytes("UTF-8"), "123".getBytes("UTF-8")); + traceInfo.put("def".getBytes("UTF-8"), "456".getBytes("UTF-8")); + builder.traceInfo(traceInfo); + List<TimelineAnnotation> timeline = new LinkedList<TimelineAnnotation>(); + timeline.add(new TimelineAnnotation(310L, "something happened")); + timeline.add(new TimelineAnnotation(380L, "something else happened")); + timeline.add(new TimelineAnnotation(390L, "more things")); + builder.timeline(timeline); + MilliSpan span = builder.build(); + String json = span.toJson(); + ObjectMapper mapper = new ObjectMapper(); + MilliSpan dspan = mapper.readValue(json, MilliSpan.class); + compareSpans(span, dspan); + } +}
