Skip to content

Commit

Permalink
Populate Zipkin remoteEndpoint (open-telemetry#4933)
Browse files Browse the repository at this point in the history
* Populate Zipkin remoteEndpoint
fixes open-telemetrygh-4932

* Add conditions for creating zipkin remote endpoint

* Parameterize remote endpoint tests with span kind

* Verify INTERNAL span kind too
  • Loading branch information
jonatan-ivanov authored and dmarkwat committed Dec 30, 2022
1 parent 625c44c commit 2afda4f
Show file tree
Hide file tree
Showing 3 changed files with 234 additions and 10 deletions.
Expand Up @@ -6,11 +6,15 @@
package io.opentelemetry.exporter.zipkin;

import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_PEER_PORT;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_SOCK_PEER_ADDR;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.PEER_SERVICE;
import static java.util.concurrent.TimeUnit.NANOSECONDS;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.AttributeType;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.resources.Resource;
Expand Down Expand Up @@ -71,8 +75,6 @@ private OtelToZipkinSpanTransformer(Supplier<InetAddress> ipAddressSupplier) {
* @return a new Zipkin Span
*/
Span generateSpan(SpanData spanData) {
Endpoint endpoint = getEndpoint(spanData);

long startTimestamp = toEpochMicros(spanData.getStartEpochNanos());
long endTimestamp = toEpochMicros(spanData.getEndEpochNanos());

Expand All @@ -84,7 +86,8 @@ Span generateSpan(SpanData spanData) {
.name(spanData.getName())
.timestamp(toEpochMicros(spanData.getStartEpochNanos()))
.duration(Math.max(1, endTimestamp - startTimestamp))
.localEndpoint(endpoint);
.localEndpoint(getLocalEndpoint(spanData))
.remoteEndpoint(getRemoteEndpoint(spanData));

if (spanData.getParentSpanContext().isValid()) {
spanBuilder.parentId(spanData.getParentSpanId());
Expand Down Expand Up @@ -140,7 +143,7 @@ private static String nullToEmpty(@Nullable String value) {
return value != null ? value : "";
}

private Endpoint getEndpoint(SpanData spanData) {
private Endpoint getLocalEndpoint(SpanData spanData) {
Attributes resourceAttributes = spanData.getResource().getAttributes();

Endpoint.Builder endpoint = Endpoint.newBuilder();
Expand All @@ -158,6 +161,30 @@ private Endpoint getEndpoint(SpanData spanData) {
return endpoint.build();
}

@Nullable
private static Endpoint getRemoteEndpoint(SpanData spanData) {
if (spanData.getKind() == SpanKind.CLIENT || spanData.getKind() == SpanKind.PRODUCER) {
// TODO: Implement fallback mechanism:
// https://opentelemetry.io/docs/reference/specification/trace/sdk_exporters/zipkin/#otlp---zipkin
Attributes attributes = spanData.getAttributes();
String serviceName = attributes.get(PEER_SERVICE);

if (serviceName != null) {
Endpoint.Builder endpoint = Endpoint.newBuilder();
endpoint.serviceName(serviceName);
endpoint.ip(attributes.get(NET_SOCK_PEER_ADDR));
Long port = attributes.get(NET_PEER_PORT);
if (port != null) {
endpoint.port(port.intValue());
}

return endpoint.build();
}
}

return null;
}

@Nullable
private static Span.Kind toSpanKind(SpanData spanData) {
switch (spanData.getKind()) {
Expand Down
Expand Up @@ -16,6 +16,9 @@
import static io.opentelemetry.exporter.zipkin.ZipkinTestUtil.spanBuilder;
import static io.opentelemetry.exporter.zipkin.ZipkinTestUtil.zipkinSpan;
import static io.opentelemetry.exporter.zipkin.ZipkinTestUtil.zipkinSpanBuilder;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_PEER_PORT;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_SOCK_PEER_ADDR;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.PEER_SERVICE;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

Expand All @@ -31,8 +34,11 @@
import java.net.InetAddress;
import java.util.Arrays;
import java.util.Collections;
import javax.annotation.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import zipkin2.Endpoint;
import zipkin2.Span;

Expand Down Expand Up @@ -135,11 +141,11 @@ void generateSpan_ResourceServiceNameMapping() {
Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "super-zipkin-service"));
SpanData data = spanBuilder().setResource(resource).build();

Endpoint expectedEndpoint =
Endpoint expectedLocalEndpoint =
Endpoint.newBuilder().serviceName("super-zipkin-service").ip(localIp).build();
Span expectedZipkinSpan =
zipkinSpan(Span.Kind.SERVER, localIp).toBuilder()
.localEndpoint(expectedEndpoint)
.localEndpoint(expectedLocalEndpoint)
.putTag(OtelToZipkinSpanTransformer.OTEL_STATUS_CODE, "OK")
.build();
assertThat(transformer.generateSpan(data)).isEqualTo(expectedZipkinSpan);
Expand All @@ -149,19 +155,204 @@ void generateSpan_ResourceServiceNameMapping() {
void generateSpan_defaultResourceServiceName() {
SpanData data = spanBuilder().setResource(Resource.empty()).build();

Endpoint expectedEndpoint =
Endpoint expectedLocalEndpoint =
Endpoint.newBuilder()
.serviceName(Resource.getDefault().getAttribute(ResourceAttributes.SERVICE_NAME))
.ip(localIp)
.build();
Span expectedZipkinSpan =
zipkinSpan(Span.Kind.SERVER, localIp).toBuilder()
.localEndpoint(expectedEndpoint)
.localEndpoint(expectedLocalEndpoint)
.putTag(OtelToZipkinSpanTransformer.OTEL_STATUS_CODE, "OK")
.build();
assertThat(transformer.generateSpan(data)).isEqualTo(expectedZipkinSpan);
}

@ParameterizedTest
@EnumSource(
value = SpanKind.class,
names = {"CLIENT", "PRODUCER"})
void generateSpan_RemoteEndpointMapping(SpanKind spanKind) {
Attributes attributes =
Attributes.builder()
.put(PEER_SERVICE, "remote-test-service")
.put(NET_SOCK_PEER_ADDR, "8.8.8.8")
.put(NET_PEER_PORT, 42L)
.build();

SpanData spanData =
spanBuilder()
.setKind(spanKind)
.setResource(Resource.empty())
.setAttributes(attributes)
.build();

Endpoint expectedLocalEndpoint =
Endpoint.newBuilder()
.serviceName(Resource.getDefault().getAttribute(ResourceAttributes.SERVICE_NAME))
.ip(localIp)
.build();

Endpoint expectedRemoteEndpoint =
Endpoint.newBuilder().serviceName("remote-test-service").ip("8.8.8.8").port(42).build();

Span expectedSpan =
zipkinSpan(toZipkinSpanKind(spanKind), localIp).toBuilder()
.localEndpoint(expectedLocalEndpoint)
.remoteEndpoint(expectedRemoteEndpoint)
.putTag(PEER_SERVICE.getKey(), "remote-test-service")
.putTag(NET_SOCK_PEER_ADDR.getKey(), "8.8.8.8")
.putTag(NET_PEER_PORT.getKey(), "42")
.putTag(OtelToZipkinSpanTransformer.OTEL_STATUS_CODE, "OK")
.build();

assertThat(transformer.generateSpan(spanData)).isEqualTo(expectedSpan);
}

@ParameterizedTest
@EnumSource(
value = SpanKind.class,
names = {"SERVER", "CONSUMER", "INTERNAL"})
void generateSpan_RemoteEndpointMappingWhenKindIsNotClientOrProducer(SpanKind spanKind) {
Attributes attributes =
Attributes.builder()
.put(PEER_SERVICE, "remote-test-service")
.put(NET_SOCK_PEER_ADDR, "8.8.8.8")
.put(NET_PEER_PORT, 42L)
.build();

SpanData spanData =
spanBuilder()
.setKind(spanKind)
.setResource(Resource.empty())
.setAttributes(attributes)
.build();

Endpoint expectedLocalEndpoint =
Endpoint.newBuilder()
.serviceName(Resource.getDefault().getAttribute(ResourceAttributes.SERVICE_NAME))
.ip(localIp)
.build();

Span expectedSpan =
zipkinSpan(toZipkinSpanKind(spanKind), localIp).toBuilder()
.localEndpoint(expectedLocalEndpoint)
.remoteEndpoint(null)
.putTag(PEER_SERVICE.getKey(), "remote-test-service")
.putTag(NET_SOCK_PEER_ADDR.getKey(), "8.8.8.8")
.putTag(NET_PEER_PORT.getKey(), "42")
.putTag(OtelToZipkinSpanTransformer.OTEL_STATUS_CODE, "OK")
.build();

assertThat(transformer.generateSpan(spanData)).isEqualTo(expectedSpan);
}

@ParameterizedTest
@EnumSource(
value = SpanKind.class,
names = {"CLIENT", "PRODUCER"})
void generateSpan_RemoteEndpointMappingWhenServiceNameIsMissing(SpanKind spanKind) {
Attributes attributes =
Attributes.builder().put(NET_SOCK_PEER_ADDR, "8.8.8.8").put(NET_PEER_PORT, 42L).build();

SpanData spanData =
spanBuilder()
.setKind(spanKind)
.setResource(Resource.empty())
.setAttributes(attributes)
.build();

Endpoint expectedLocalEndpoint =
Endpoint.newBuilder()
.serviceName(Resource.getDefault().getAttribute(ResourceAttributes.SERVICE_NAME))
.ip(localIp)
.build();

Span expectedSpan =
zipkinSpan(toZipkinSpanKind(spanKind), localIp).toBuilder()
.localEndpoint(expectedLocalEndpoint)
.remoteEndpoint(null)
.putTag(NET_SOCK_PEER_ADDR.getKey(), "8.8.8.8")
.putTag(NET_PEER_PORT.getKey(), "42")
.putTag(OtelToZipkinSpanTransformer.OTEL_STATUS_CODE, "OK")
.build();

assertThat(transformer.generateSpan(spanData)).isEqualTo(expectedSpan);
}

@ParameterizedTest
@EnumSource(
value = SpanKind.class,
names = {"CLIENT", "PRODUCER"})
void generateSpan_RemoteEndpointMappingWhenPortIsMissing(SpanKind spanKind) {
Attributes attributes =
Attributes.builder()
.put(PEER_SERVICE, "remote-test-service")
.put(NET_SOCK_PEER_ADDR, "8.8.8.8")
.build();

SpanData spanData =
spanBuilder()
.setKind(spanKind)
.setResource(Resource.empty())
.setAttributes(attributes)
.build();

Endpoint expectedLocalEndpoint =
Endpoint.newBuilder()
.serviceName(Resource.getDefault().getAttribute(ResourceAttributes.SERVICE_NAME))
.ip(localIp)
.build();

Endpoint expectedRemoteEndpoint =
Endpoint.newBuilder().serviceName("remote-test-service").ip("8.8.8.8").build();

Span expectedSpan =
zipkinSpan(toZipkinSpanKind(spanKind), localIp).toBuilder()
.localEndpoint(expectedLocalEndpoint)
.remoteEndpoint(expectedRemoteEndpoint)
.putTag(PEER_SERVICE.getKey(), "remote-test-service")
.putTag(NET_SOCK_PEER_ADDR.getKey(), "8.8.8.8")
.putTag(OtelToZipkinSpanTransformer.OTEL_STATUS_CODE, "OK")
.build();

assertThat(transformer.generateSpan(spanData)).isEqualTo(expectedSpan);
}

@ParameterizedTest
@EnumSource(
value = SpanKind.class,
names = {"CLIENT", "PRODUCER"})
void generateSpan_RemoteEndpointMappingWhenIpAndPortAreMissing(SpanKind spanKind) {
Attributes attributes = Attributes.builder().put(PEER_SERVICE, "remote-test-service").build();

SpanData spanData =
spanBuilder()
.setKind(spanKind)
.setResource(Resource.empty())
.setAttributes(attributes)
.build();

Endpoint expectedLocalEndpoint =
Endpoint.newBuilder()
.serviceName(Resource.getDefault().getAttribute(ResourceAttributes.SERVICE_NAME))
.ip(localIp)
.build();

Endpoint expectedRemoteEndpoint =
Endpoint.newBuilder().serviceName("remote-test-service").build();

Span expectedSpan =
zipkinSpan(toZipkinSpanKind(spanKind), localIp).toBuilder()
.localEndpoint(expectedLocalEndpoint)
.remoteEndpoint(expectedRemoteEndpoint)
.putTag(PEER_SERVICE.getKey(), "remote-test-service")
.putTag(OtelToZipkinSpanTransformer.OTEL_STATUS_CODE, "OK")
.build();

assertThat(transformer.generateSpan(spanData)).isEqualTo(expectedSpan);
}

@Test
void generateSpan_WithAttributes() {
Attributes attributes =
Expand Down Expand Up @@ -304,4 +495,9 @@ void generateSpan_WithRpcUnsetStatus() {
.putTag(SemanticAttributes.RPC_SERVICE.getKey(), "my service name")
.build());
}

@Nullable
private static Span.Kind toZipkinSpanKind(SpanKind spanKind) {
return spanKind != SpanKind.INTERNAL ? Span.Kind.valueOf(spanKind.name()) : null;
}
}
Expand Up @@ -19,6 +19,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
import zipkin2.Endpoint;
import zipkin2.Span;

Expand Down Expand Up @@ -60,11 +61,11 @@ static TestSpanData.Builder spanBuilder() {
.setHasEnded(true);
}

static Span zipkinSpan(Span.Kind kind, InetAddress localIp) {
static Span zipkinSpan(@Nullable Span.Kind kind, InetAddress localIp) {
return zipkinSpanBuilder(kind, localIp).build();
}

static Span.Builder zipkinSpanBuilder(Span.Kind kind, InetAddress localIp) {
static Span.Builder zipkinSpanBuilder(@Nullable Span.Kind kind, InetAddress localIp) {
return Span.newBuilder()
.traceId(TRACE_ID)
.parentId(PARENT_SPAN_ID)
Expand Down

0 comments on commit 2afda4f

Please sign in to comment.