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 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,46 @@
/*
* 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.
*/
class EventDataToAnnotation {

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

private static String toJson(Attributes attributes) {
return attributes.asMap().entrySet().stream()
.map(entry -> "\"" + entry.getKey() + "\":" + toValue(entry.getValue()))
breedx-splk marked this conversation as resolved.
Show resolved Hide resolved
.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\":{}");
}
}