Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
* Logging: Output event name in `SystemOutLogRecordExporter`
([#8609](https://github.com/open-telemetry/opentelemetry-java/pull/8609))

### Shims

* Add OpenCensus links to the OpenTelemetry span instead of logging a warning and dropping them
([#8635](https://github.com/open-telemetry/opentelemetry-java/pull/8635))

## Version 1.64.0 (2026-07-10)

### API
Expand Down
5 changes: 0 additions & 5 deletions opencensus-shim/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,3 @@ SdkMeterProvider.builder()
.registerMetricReader(OpenCensusMetrics.attachTo(PeriodicMetricReader.create(metricExporter)))
.build();
```

## Known Problems

* OpenCensus links added after an OpenCensus span is created will not be
exported, as OpenTelemetry only supports links added when a span is created.
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,21 @@
import io.opencensus.trace.MessageEvent;
import io.opencensus.trace.Span;
import io.opencensus.trace.Status;
import io.opencensus.trace.TraceOptions;
import io.opencensus.trace.Tracestate;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.api.trace.StatusCode;
import java.util.EnumSet;
import java.util.Map;
import java.util.logging.Logger;

class OpenTelemetrySpanImpl extends Span
implements io.opentelemetry.api.trace.Span, DelegatingSpan {
private static final Logger LOGGER = Logger.getLogger(OpenTelemetrySpanImpl.class.getName());
private static final EnumSet<Span.Options> RECORD_EVENTS_SPAN_OPTIONS =
EnumSet.of(Span.Options.RECORD_EVENTS);
private static final Tracestate OC_TRACESTATE_DEFAULT = Tracestate.builder().build();

private final io.opentelemetry.api.trace.Span otelSpan;

Expand Down Expand Up @@ -104,7 +105,20 @@ public void addAnnotation(Annotation annotation) {

@Override
public void addLink(Link link) {
LOGGER.warning("OpenTelemetry does not support links added after a span is created.");
Preconditions.checkNotNull(link, "link");
AttributesBuilder attributesBuilder = Attributes.builder();
mapAttributes(link.getAttributes(), attributesBuilder);
// DelegatingSpan does not override addLink, so DelegatingSpan.super.addLink(..) would call the
// no-op default method of Span and drop the link. Call the delegate directly instead.
getDelegate()
.addLink(
mapSpanContext(
io.opencensus.trace.SpanContext.create(
link.getTraceId(),
link.getSpanId(),
TraceOptions.DEFAULT,
OC_TRACESTATE_DEFAULT)),
attributesBuilder.build());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,27 @@

package io.opentelemetry.opencensusshim;

import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import io.opencensus.trace.AttributeValue;
import io.opencensus.trace.Link;
import io.opencensus.trace.SpanId;
import io.opencensus.trace.Status;
import io.opencensus.trace.TraceId;
import io.opencensus.trace.TraceOptions;
import io.opencensus.trace.Tracestate;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.api.trace.TraceFlags;
import io.opentelemetry.api.trace.TraceState;
import java.util.Collections;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
Expand All @@ -26,6 +37,9 @@
@MockitoSettings(strictness = Strictness.LENIENT)
class OpenTelemetrySpanImplTest {

private static final String TRACE_ID = "0123456789abcdef0123456789abcdef";
private static final String SPAN_ID = "fedcba9876543210";

@Mock private Span otelSpan;

private OpenTelemetrySpanImpl shimSpan() {
Expand All @@ -47,4 +61,35 @@ void setStatus_withoutDescription_setsCodeOnly() {
verify(otelSpan).setStatus(StatusCode.OK);
verify(otelSpan, never()).setStatus(any(StatusCode.class), anyString());
}

@Test
void addLink_delegatesToOtelSpan() {
shimSpan().addLink(Link.fromSpanContext(ocSpanContext(), Link.Type.CHILD_LINKED_SPAN));

verify(otelSpan).addLink(expectedOtelSpanContext(), Attributes.empty());
}

@Test
void addLink_mapsAttributes() {
shimSpan()
.addLink(
Link.fromSpanContext(
ocSpanContext(),
Link.Type.PARENT_LINKED_SPAN,
Collections.singletonMap("key", AttributeValue.stringAttributeValue("value"))));

verify(otelSpan).addLink(expectedOtelSpanContext(), Attributes.of(stringKey("key"), "value"));
}

private static io.opencensus.trace.SpanContext ocSpanContext() {
return io.opencensus.trace.SpanContext.create(
TraceId.fromLowerBase16(TRACE_ID),
SpanId.fromLowerBase16(SPAN_ID),
TraceOptions.DEFAULT,
Tracestate.builder().build());
}

private static SpanContext expectedOtelSpanContext() {
return SpanContext.create(TRACE_ID, SPAN_ID, TraceFlags.getDefault(), TraceState.getDefault());
}
}
Loading