Skip to content

Commit

Permalink
Support adding static resource attributes on the encoder side (#18)
Browse files Browse the repository at this point in the history
This pull request adds a way to add static resource attributes on the
encoder side.
Related to #14, resource attributes are typically static and do not
depend on a span.

For example, Spring Boot provides
`management.opentelemetry.resource-attributes.*` properties to add
resource attributes for Otel SDK. This pr will provide the feature
parity of this capability for Brave.

https://github.com/spring-projects/spring-boot/blob/aba22547ee56606c815ecb1ef57cee82e9005ed9/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/OpenTelemetryAutoConfiguration.java#L74-L91
  • Loading branch information
making authored Sep 26, 2024
1 parent ed5e1f7 commit f493e26
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,75 @@
*/
package zipkin2.reporter.otel.brave;

import java.util.Collections;
import java.util.Map;

import brave.Tag;
import brave.Tags;
import brave.handler.MutableSpan;
import io.opentelemetry.proto.trace.v1.TracesData;
import zipkin2.reporter.BytesEncoder;
import zipkin2.reporter.Encoding;

public final class OtlpProtoV1Encoder implements BytesEncoder<MutableSpan> {
public static OtlpProtoV1Encoder create() {
return newBuilder().build();
}

public static Builder newBuilder() {
return new Builder();
}

public static final class Builder {
Tag<Throwable> errorTag = Tags.ERROR;

Map<String, String> resourceAttributes = Collections.emptyMap();

/** The throwable parser. Defaults to {@link Tags#ERROR}. */
public Builder errorTag(Tag<Throwable> errorTag) {
if (errorTag == null) {
throw new NullPointerException("errorTag == null");
}
this.errorTag = errorTag; return this;
}

/** static resource attributes added to a {@link io.opentelemetry.proto.resource.v1.Resource}. Defaults to empty map. */
public Builder resourceAttributes(Map<String, String> resourceAttributes) {
if (resourceAttributes == null) {
throw new NullPointerException("resourceAttributes == null");
}
this.resourceAttributes = resourceAttributes; return this;
}

public OtlpProtoV1Encoder build() {
return new OtlpProtoV1Encoder(this);
}

Builder() {

}
}

final SpanTranslator spanTranslator;

public OtlpProtoV1Encoder(Tag<Throwable> errorTag) {
if (errorTag == null) throw new NullPointerException("errorTag == null");
this.spanTranslator = new SpanTranslator(errorTag);
private OtlpProtoV1Encoder(Builder builder) {
this.spanTranslator = new SpanTranslator(builder.errorTag, builder.resourceAttributes);
}

@Override
public Encoding encoding() {
return Encoding.PROTO3;
}

@Override public int sizeInBytes(MutableSpan span) {
@Override
public int sizeInBytes(MutableSpan span) {
// TODO: Create a proto size function to avoid allocations here
TracesData convert = translate(span);
return encoding().listSizeInBytes(convert.getSerializedSize());
}

@Override public byte[] encode(MutableSpan span) {
@Override
public byte[] encode(MutableSpan span) {
return translate(span).toByteArray();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import io.opentelemetry.proto.common.v1.AnyValue;
import io.opentelemetry.proto.common.v1.InstrumentationScope;
import io.opentelemetry.proto.common.v1.KeyValue;
import io.opentelemetry.proto.resource.v1.Resource;
import io.opentelemetry.proto.trace.v1.ResourceSpans;
import io.opentelemetry.proto.trace.v1.ResourceSpans.Builder;
import io.opentelemetry.proto.trace.v1.ScopeSpans;
Expand Down Expand Up @@ -78,8 +79,11 @@ final class SpanTranslator {

private final TagMapper tagMapper;

SpanTranslator(Tag<Throwable> errorTag) {
private final Map<String, String> resourceAttributes;

SpanTranslator(Tag<Throwable> errorTag, Map<String, String> resourceAttributes) {
this.tagMapper = new TagMapper(errorTag, TAG_TO_ATTRIBUTE);
this.resourceAttributes = resourceAttributes;
}

TracesData translate(MutableSpan braveSpan) {
Expand All @@ -101,7 +105,7 @@ private Span.Builder builderForSingleSpan(MutableSpan span, Builder resourceSpan
Span.Builder spanBuilder = Span.newBuilder()
.setTraceId(span.traceId() != null ? ByteString.fromHex(span.traceId()) : INVALID_TRACE_ID)
.setSpanId(span.id() != null ? ByteString.fromHex(span.id()) : INVALID_SPAN_ID)
.setName(span.name() == null || span.name().isEmpty() ? DEFAULT_SPAN_NAME : span.name());
.setName(span.name() == null || span.name().isEmpty() ? DEFAULT_SPAN_NAME : span.name());
if (span.parentId() != null) {
spanBuilder.setParentSpanId(ByteString.fromHex(span.parentId()));
}
Expand All @@ -110,11 +114,15 @@ private Span.Builder builderForSingleSpan(MutableSpan span, Builder resourceSpan
spanBuilder.setStartTimeUnixNano(TimeUnit.MICROSECONDS.toNanos(start));
spanBuilder.setEndTimeUnixNano(TimeUnit.MICROSECONDS.toNanos(finish));
spanBuilder.setKind(translateKind(span.kind()));
String localServiceName = span.localServiceName();
if (localServiceName == null || localServiceName.isEmpty()) {
localServiceName = DEFAULT_SERVICE_NAME;
Resource.Builder resourceBuilder = resourceSpansBuilder.getResourceBuilder();
if (!this.resourceAttributes.containsKey(ServiceAttributes.SERVICE_NAME.getKey())) {
String localServiceName = span.localServiceName();
if (localServiceName == null || localServiceName.isEmpty()) {
localServiceName = DEFAULT_SERVICE_NAME;
}
resourceBuilder.addAttributes(stringAttribute(ServiceAttributes.SERVICE_NAME.getKey(), localServiceName));
}
resourceSpansBuilder.getResourceBuilder().addAttributes(stringAttribute(ServiceAttributes.SERVICE_NAME.getKey(), localServiceName));
resourceAttributes.forEach((k, v) -> resourceBuilder.addAttributes(stringAttribute(k, v)));
maybeAddStringAttribute(spanBuilder, NetworkAttributes.NETWORK_LOCAL_ADDRESS.getKey(), span.localIp());
maybeAddIntAttribute(spanBuilder, NetworkAttributes.NETWORK_LOCAL_PORT.getKey(), span.localPort());
maybeAddStringAttribute(spanBuilder, NetworkAttributes.NETWORK_PEER_ADDRESS.getKey(), span.remoteIp());
Expand All @@ -123,7 +131,6 @@ private Span.Builder builderForSingleSpan(MutableSpan span, Builder resourceSpan
span.forEachTag(tagMapper, spanBuilder);
span.forEachAnnotation(tagMapper, spanBuilder);
tagMapper.addErrorTag(spanBuilder, span);

return spanBuilder;
}

Expand All @@ -145,16 +152,16 @@ private static SpanKind translateKind(Kind kind) {

static KeyValue stringAttribute(String key, String value) {
return KeyValue.newBuilder()
.setKey(key)
.setValue(AnyValue.newBuilder().setStringValue(value))
.build();
.setKey(key)
.setValue(AnyValue.newBuilder().setStringValue(value))
.build();
}

static KeyValue intAttribute(String key, int value) {
return KeyValue.newBuilder()
.setKey(key)
.setValue(AnyValue.newBuilder().setIntValue(value))
.build();
.setKey(key)
.setValue(AnyValue.newBuilder().setIntValue(value))
.build();
}

private static void maybeAddStringAttribute(Span.Builder spanBuilder, String key, String value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ void beforeEach() {
static Stream<Arguments> encoderAndEndpoint() {
return Stream.of(
/* existing sender + new encoder -> otlp with otel collector */
Arguments.of(Encoding.PROTO3, new OtlpProtoV1Encoder(Tags.ERROR), String.format("http://localhost:%d/v1/traces", collector.getMappedPort(COLLECTOR_OTLP_HTTP_PORT))),
Arguments.of(Encoding.PROTO3, OtlpProtoV1Encoder.create(), String.format("http://localhost:%d/v1/traces", collector.getMappedPort(COLLECTOR_OTLP_HTTP_PORT))),
/* existing sender + new encoder -> otlp without otel collector */
Arguments.of(Encoding.PROTO3, new OtlpProtoV1Encoder(Tags.ERROR), String.format("http://localhost:%d/v1/traces", otlpHttpServer.httpPort())),
Arguments.of(Encoding.PROTO3, OtlpProtoV1Encoder.create(), String.format("http://localhost:%d/v1/traces", otlpHttpServer.httpPort())),
/* existing sender + zipkin encoder -> zipkin endpoint on collector */
Arguments.of(Encoding.JSON, MutableSpanBytesEncoder.create(Encoding.JSON, Tags.ERROR), String.format("http://localhost:%d/api/v2/spans", collector.getMappedPort(COLLECTOR_ZIPKIN_PORT))));
}
Expand Down
Loading

0 comments on commit f493e26

Please sign in to comment.