SpanParser { public static String convertSpanToJSON(Span span) { StringBuilder builder = new StringBuilder(); builder.append("{\"").append(TRACE_ID_FIELD).append("\":\"").append(escapeJson(span.getTraceId())).append('\"'); builder.append(",\"").append(PARENT_SPAN_ID_FIELD).append("\":\"").append(escapeJson(span.getParentSpanId())).append('\"'); builder.append(",\"").append(SPAN_ID_FIELD).append("\":\"").append(escapeJson(span.getSpanId())).append('\"'); builder.append(",\"").append(SPAN_NAME_FIELD).append("\":\"").append(escapeJson(span.getSpanName())).append('\"'); builder.append(",\"").append(SAMPLEABLE_FIELD).append("\":\"").append(span.isSampleable()).append('\"'); builder.append(",\"").append(USER_ID_FIELD).append("\":\"").append(escapeJson(span.getUserId())).append('\"'); builder.append(",\"").append(SPAN_PURPOSE_FIELD).append("\":\"").append(span.getSpanPurpose().name()).append('\"'); builder.append(",\"").append(START_TIME_EPOCH_MICROS_FIELD).append("\":\"").append(span.getSpanStartTimeEpochMicros()).append('\"'); if (span.isCompleted()) { builder.append(",\"").append(DURATION_NANOS_FIELD).append("\":\"").append(span.getDurationNanos()).append('\"'); } if(!span.getTags().isEmpty()) { builder.append(",\"").append(TAGS_FIELD).append("\":{"); boolean first = true; for (Map.Entry<String, String> tagEntry : span.getTags().entrySet()) { if (!first) { builder.append(','); } String escapedKey = escapeJson(tagEntry.getKey()); String escapedValue = escapeJson(tagEntry.getValue()); builder.append('\"').append(escapedKey).append("\":\"").append(escapedValue).append('\"'); first = false; } builder.append("}"); } if (!span.getTimestampedAnnotations().isEmpty()) { builder.append(",\"").append(ANNOTATIONS_LIST_FIELD).append("\":["); boolean first = true; for (TimestampedAnnotation annotation : span.getTimestampedAnnotations()) { if (!first) { builder.append(','); } String escapedValue = escapeJson(annotation.getValue()); builder.append("{\"").append(ANNOTATION_SUBOBJECT_TIMESTAMP_FIELD) .append("\":\"").append(annotation.getTimestampEpochMicros()).append('\"'); builder.append(",\"").append(ANNOTATION_SUBOBJECT_VALUE_FIELD) .append("\":\"").append(escapedValue).append("\"}"); first = false; } builder.append("]"); } builder.append("}"); return builder.toString(); } protected  SpanParser(); static String convertSpanToJSON(Span span); static Span fromJSON(String json); static String convertSpanToKeyValueFormat(Span span); static Span fromKeyValueString(String keyValueStr); static String escapeJson(String orig); static String unescapeJson(String escaped); static final String TRACE_ID_FIELD; static final String PARENT_SPAN_ID_FIELD; static final String SPAN_ID_FIELD; static final String SPAN_NAME_FIELD; static final String SAMPLEABLE_FIELD; static final String USER_ID_FIELD; static final String SPAN_PURPOSE_FIELD; static final String START_TIME_EPOCH_MICROS_FIELD; static final String DURATION_NANOS_FIELD; static final String TAGS_FIELD; static final String ANNOTATIONS_LIST_FIELD; static final String ANNOTATION_SUBOBJECT_TIMESTAMP_FIELD; static final String ANNOTATION_SUBOBJECT_VALUE_FIELD; static final String KEY_VALUE_TAG_PREFIX; static final String KEY_VALUE_TIMESTAMPED_ANNOTATION_PREFIX; }
@UseDataProvider("tagAndAnnotationScenarioDataProvider") @Test public void convertSpanToJSON_should_function_properly_when_there_are_no_null_values( TagAndAnnotationScenario scenario ) throws IOException { Span validSpan = createFilledOutSpan(true, scenario.tags, scenario.annotations); assertThat(validSpan.getTraceId()).isNotEmpty(); assertThat(validSpan.getUserId()).isNotEmpty(); assertThat(validSpan.getParentSpanId()).isNotEmpty(); assertThat(validSpan.getSpanName()).isNotEmpty(); assertThat(validSpan.getSpanId()).isNotEmpty(); assertThat(validSpan.getDurationNanos()).isNotNull(); assertThat(validSpan.isCompleted()).isTrue(); assertThat(validSpan.getSpanPurpose()).isNotNull(); assertThat(validSpan.getTags()).isEqualTo(scenario.tags); assertThat(validSpan.getTimestampedAnnotations()).isEqualTo(scenario.annotations); String json = SpanParser.convertSpanToJSON(validSpan); Map<String, Object> spanValuesFromJackson = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {}); verifySpanEqualsDeserializedValues(validSpan, spanValuesFromJackson); }
@Test public void convertSpanToJSON_should_function_properly_when_there_are_null_values() throws IOException { Span validSpan = Span.generateRootSpanForNewTrace(spanName, null).build(); assertThat(validSpan.getParentSpanId()).isNull(); assertThat(validSpan.getUserId()).isNull(); assertThat(validSpan.getDurationNanos()).isNull(); String json = SpanParser.convertSpanToJSON(validSpan); Map<String, Object> spanValuesFromJackson = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {}); verifySpanEqualsDeserializedValues(validSpan, spanValuesFromJackson); }
@Test public void convertSpanToJSON_should_function_properly_for_non_completed_spans() throws IOException { Span validSpan = Span.generateRootSpanForNewTrace(spanName, spanPurpose).build(); String json = SpanParser.convertSpanToJSON(validSpan); assertThat(validSpan.isCompleted()).isFalse(); assertThat(validSpan.getDurationNanos()).isNull(); Map<String, Object> spanValuesFromJackson = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {}); verifySpanEqualsDeserializedValues(validSpan, spanValuesFromJackson); }
@Test public void convertSpanToJSON_should_function_properly_for_completed_spans() throws IOException { Span validSpan = Span.generateRootSpanForNewTrace(spanName, spanPurpose).build(); completeSpan(validSpan); assertThat(validSpan.isCompleted()).isTrue(); assertThat(validSpan.getDurationNanos()).isNotNull(); String json = SpanParser.convertSpanToJSON(validSpan); Map<String, Object> spanValuesFromJackson = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {}); verifySpanEqualsDeserializedValues(validSpan, spanValuesFromJackson); }