@Override public void write(MutableSpan span, WriteBuffer b) {
    b.writeByte('{');
    boolean wroteField = false;
    if (span.traceId() != null) {
      wroteField = writeFieldBegin(b, "traceId", wroteField);
      b.writeByte('"');
      b.writeAscii(span.traceId());
      b.writeByte('"');
    }
    if (span.parentId() != null) {
      wroteField = writeFieldBegin(b, "parentId", wroteField);
      b.writeByte('"');
      b.writeAscii(span.parentId());
      b.writeByte('"');
    }
    if (span.id() != null) {
      wroteField = writeFieldBegin(b, "id", wroteField);
      b.writeByte('"');
      b.writeAscii(span.id());
      b.writeByte('"');
    }
    if (span.kind() != null) {
      wroteField = writeFieldBegin(b, "kind", wroteField);
      b.writeByte('"');
      b.writeAscii(span.kind().toString());
      b.writeByte('"');
    }
    if (span.name() != null) {
      wroteField = writeFieldBegin(b, "name", wroteField);
      b.writeByte('"');
      jsonEscape(span.name(), b);
      b.writeByte('"');
    }
    long startTimestamp = span.startTimestamp(), finishTimestamp = span.finishTimestamp();
    if (startTimestamp != 0L) {
      wroteField = writeFieldBegin(b, "timestamp", wroteField);
      b.writeAscii(startTimestamp);
      if (finishTimestamp != 0L) {
        wroteField = writeFieldBegin(b, "duration", wroteField);
        b.writeAscii(finishTimestamp - startTimestamp);
      }
    }
    if (span.localServiceName() != null || span.localIp() != null) {
      wroteField = writeFieldBegin(b, "localEndpoint", wroteField);
      writeEndpoint(b, span.localServiceName(), span.localIp(), span.localPort());
    }
    if (span.remoteServiceName() != null || span.remoteIp() != null) {
      wroteField = writeFieldBegin(b, "remoteEndpoint", wroteField);
      writeEndpoint(b, span.remoteServiceName(), span.remoteIp(), span.remotePort());
    }
    int annotationLength = span.annotationCount();
    if (annotationLength > 0) {
      wroteField = writeFieldBegin(b, "annotations", wroteField);
      b.writeByte('[');
      for (int i = 0; i < annotationLength; ) {
        long timestamp = span.annotationTimestampAt(i);
        String value = span.annotationValueAt(i);
        writeAnnotation(timestamp, value, b);
        if (++i < annotationLength) b.writeByte(',');
      }
      b.writeByte(']');
    }
    int tagCount = span.tagCount();
    String errorValue = errorTag.value(span.error(), null);
    String errorTagName = errorValue != null ? errorTag.key() : null;
    boolean writeError = errorTagName != null;
    if (tagCount > 0 || writeError) {
      wroteField = writeFieldBegin(b, "tags", wroteField);
      b.writeByte('{');
      for (int i = 0; i < tagCount; ) {
        String key = span.tagKeyAt(i);
        if (writeError && key.equals(errorTagName)) writeError = false;
        writeKeyValue(b, key, span.tagValueAt(i));
        if (++i < tagCount) b.writeByte(',');
      }
      if (writeError) {
        if (tagCount > 0) b.writeByte(',');
        writeKeyValue(b, errorTagName, errorValue);
      }
      b.writeByte('}');
    }
    if (Boolean.TRUE.equals(span.debug())) {
      wroteField = writeFieldBegin(b, "debug", wroteField);
      b.writeAscii("true");
    }
    if (Boolean.TRUE.equals(span.shared())) {
      writeFieldBegin(b, "shared", wroteField);
      b.writeAscii("true");
    }
    b.writeByte('}');
  }
@Test void writeClientSpan() {
    jsonWriter.write(clientSpan, buffer);

    assertThat(buffer.toString()).isEqualTo("{"
      + "\"traceId\":\"0000000000000001\",\"parentId\":\"0000000000000002\",\"id\":\"0000000000000003\","
      + "\"kind\":\"CLIENT\",\"name\":\"get\",\"timestamp\":1000,\"duration\":200,"
      + "\"localEndpoint\":{\"serviceName\":\"frontend\",\"ipv4\":\"127.0.0.1\"},"
      + "\"remoteEndpoint\":{\"serviceName\":\"backend\",\"ipv4\":\"192.168.99.101\",\"port\":9000},"
      + "\"annotations\":[{\"timestamp\":1100,\"value\":\"foo\"}],"
      + "\"tags\":{\"http.path\":\"/api\",\"clnt/finagle.version\":\"6.45.0\"}"
      + "}");
  }