Skip to content

Commit

Permalink
Zipkin exporter: Serialize EventData attributes as JSON (open-telemet…
Browse files Browse the repository at this point in the history
…ry#4934)

* serialize EventData attributes to json

* remove import

* fix test

* address code review comments.

* safety first
  • Loading branch information
breedx-splk authored and dmarkwat committed Dec 30, 2022
1 parent af6f940 commit 4dbb530
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 8 deletions.
@@ -0,0 +1,48 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.exporter.zipkin;

import static java.util.stream.Collectors.joining;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.trace.data.EventData;
import java.util.List;

/**
* Converts an EventData instance to a String representation of that data, with attributes converted
* to JSON.
*
* <p>See <a
* href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/zipkin.md#events">the
* zipkin exporter spec</a> for details.
*/
final class EventDataToAnnotation {

private EventDataToAnnotation() {}

static String apply(EventData eventData) {
String name = eventData.getName();
String value = toJson(eventData.getAttributes());
return "\"" + name + "\":" + value;
}

private static String toJson(Attributes attributes) {
return attributes.asMap().entrySet().stream()
.map(entry -> "\"" + entry.getKey() + "\":" + toValue(entry.getValue()))
.collect(joining(",", "{", "}"));
}

private static String toValue(Object o) {
if (o instanceof String) {
return "\"" + o + "\"";
}
if (o instanceof List) {
return ((List<?>) o)
.stream().map(EventDataToAnnotation::toValue).collect(joining(",", "[", "]"));
}
return String.valueOf(o);
}
}
Expand Up @@ -41,7 +41,6 @@ final class OtelToZipkinSpanTransformer {
static final String OTEL_STATUS_CODE = "otel.status_code";
static final AttributeKey<String> STATUS_ERROR = stringKey("error");
private final Supplier<InetAddress> ipAddressSupplier;

/**
* Creates an instance of an OtelToZipkinSpanTransformer with the given Supplier that can produce
* an InetAddress, which may be null. This value from this Supplier will be used when creating the
Expand Down Expand Up @@ -125,8 +124,9 @@ Span generateSpan(SpanData spanData) {
KEY_INSTRUMENTATION_LIBRARY_VERSION, instrumentationScopeInfo.getVersion());
}

for (EventData annotation : spanData.getEvents()) {
spanBuilder.addAnnotation(toEpochMicros(annotation.getEpochNanos()), annotation.getName());
for (EventData eventData : spanData.getEvents()) {
String annotation = EventDataToAnnotation.apply(eventData);
spanBuilder.addAnnotation(toEpochMicros(eventData.getEpochNanos()), annotation);
}
int droppedEvents = spanData.getTotalRecordedEvents() - spanData.getEvents().size();
if (droppedEvents > 0) {
Expand All @@ -136,7 +136,7 @@ Span generateSpan(SpanData spanData) {
return spanBuilder.build();
}

private static String nullToEmpty(String value) {
private static String nullToEmpty(@Nullable String value) {
return value != null ? value : "";
}

Expand Down
@@ -0,0 +1,49 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.exporter.zipkin;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.trace.data.EventData;
import org.junit.jupiter.api.Test;

class EventDataToAnnotationTest {

@Test
void basicConversion() {

Attributes attrs =
Attributes.builder()
.put("v1", "v1")
.put("v2", 12L)
.put("v3", 123.45)
.put("v4", false)
.put("v5", "foo", "bar", "baz")
.put("v6", 1, 2, 3)
.put("v7", 1.23, 3.45)
.put("v8", true, false, true)
.build();
String expected =
"\"cat\":{\"v1\":\"v1\",\"v2\":12,\"v3\":123.45,\"v4\":false,\"v5\":[\"foo\",\"bar\",\"baz\"],\"v6\":[1,2,3],\"v7\":[1.23,3.45],\"v8\":[true,false,true]}";
EventData eventData = EventData.create(0, "cat", attrs);

String result = EventDataToAnnotation.apply(eventData);

assertThat(result).isEqualTo(expected);
}

@Test
void empty() {
Attributes attrs = Attributes.empty();
String expected = "\"dog\":{}";
EventData eventData = EventData.create(0, "dog", attrs);

String result = EventDataToAnnotation.apply(eventData);

assertThat(result).isEqualTo(expected);
}
}
Expand Up @@ -230,8 +230,8 @@ private static Span buildZipkinSpan(InetAddress localAddress, String traceId) {
.timestamp(START_EPOCH_NANOS / 1000)
.duration((END_EPOCH_NANOS / 1000) - (START_EPOCH_NANOS / 1000))
.localEndpoint(Endpoint.newBuilder().serviceName(SERVICE_NAME).ip(localAddress).build())
.addAnnotation(RECEIVED_TIMESTAMP_NANOS / 1000, "RECEIVED")
.addAnnotation(SENT_TIMESTAMP_NANOS / 1000, "SENT")
.addAnnotation(RECEIVED_TIMESTAMP_NANOS / 1000, "\"RECEIVED\":{}")
.addAnnotation(SENT_TIMESTAMP_NANOS / 1000, "\"SENT\":{}")
.putTag(OtelToZipkinSpanTransformer.OTEL_STATUS_CODE, "OK")
.build();
}
Expand Down
Expand Up @@ -74,7 +74,7 @@ static Span.Builder zipkinSpanBuilder(Span.Kind kind, InetAddress localIp) {
.timestamp(1505855794000000L + 194009601L / 1000)
.duration((1505855799000000L + 465726528L / 1000) - (1505855794000000L + 194009601L / 1000))
.localEndpoint(Endpoint.newBuilder().ip(localIp).serviceName("tweetiebird").build())
.addAnnotation(1505855799000000L + 433901068L / 1000, "RECEIVED")
.addAnnotation(1505855799000000L + 459486280L / 1000, "SENT");
.addAnnotation(1505855799000000L + 433901068L / 1000, "\"RECEIVED\":{}")
.addAnnotation(1505855799000000L + 459486280L / 1000, "\"SENT\":{}");
}
}

0 comments on commit 4dbb530

Please sign in to comment.