Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zipkin exporter: Serialize EventData attributes as JSON #4934

Merged
merged 5 commits into from
Nov 21, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.exporter.zipkin;

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

/**
* 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.
*/
class EventDataToAnnotation implements Function<EventData, String> {

@Override
public String apply(EventData eventData) {
breedx-splk marked this conversation as resolved.
Show resolved Hide resolved
String name = eventData.getName();
String value = toJson(eventData.getAttributes());
breedx-splk marked this conversation as resolved.
Show resolved Hide resolved
return "\"" + name + "\":" + value;
}

private String toJson(Attributes attributes) {
breedx-splk marked this conversation as resolved.
Show resolved Hide resolved
StringBuilder sb = new StringBuilder("{");
attributes.forEach(
(key, o) -> {
if (sb.length() > 1) {
sb.append(",");
}
sb.append("\"").append(key.getKey()).append("\":");
String value = toValue(o);
sb.append(value);
});
sb.append("}");
return sb.toString();
breedx-splk marked this conversation as resolved.
Show resolved Hide resolved
}

private String toValue(Object o) {
StringBuilder sb = new StringBuilder();
if (o instanceof String) {
sb.append("\"").append(o).append("\"");
} else if (o instanceof List) {
sb.append("[");
((List<?>) o)
.forEach(
v -> {
if (sb.length() > 1) {
sb.append(",");
}
sb.append(toValue(v));
});
sb.append("]");
} else {
sb.append(o);
}
return sb.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.net.InetAddress;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import zipkin2.Endpoint;
Expand All @@ -41,7 +42,7 @@ 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;

private final Function<EventData, String> eventDataToAnnotation = new EventDataToAnnotation();
/**
* 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 +126,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 +138,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
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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);

EventDataToAnnotation converter = new EventDataToAnnotation();

String result = converter.apply(eventData);

assertThat(result).isEqualTo(expected);
}

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

EventDataToAnnotation converter = new EventDataToAnnotation();

String result = converter.apply(eventData);

assertThat(result).isEqualTo(expected);
}
}
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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\":{}");
}
}