Skip to content

Commit e6f64e9

Browse files
committed
Add links to OpenCensus spans after span start
1 parent 09d6c17 commit e6f64e9

4 files changed

Lines changed: 67 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
* Logging: Output event name in `SystemOutLogRecordExporter`
1010
([#8609](https://github.com/open-telemetry/opentelemetry-java/pull/8609))
1111

12+
### Shims
13+
14+
* Add OpenCensus links to the OpenTelemetry span instead of logging a warning and dropping them
15+
([#8635](https://github.com/open-telemetry/opentelemetry-java/pull/8635))
16+
1217
## Version 1.64.0 (2026-07-10)
1318

1419
### API

opencensus-shim/README.md

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,3 @@ SdkMeterProvider.builder()
3838
.registerMetricReader(OpenCensusMetrics.attachTo(PeriodicMetricReader.create(metricExporter)))
3939
.build();
4040
```
41-
42-
## Known Problems
43-
44-
* OpenCensus links added after an OpenCensus span is created will not be
45-
exported, as OpenTelemetry only supports links added when a span is created.

opencensus-shim/src/main/java/io/opentelemetry/opencensusshim/OpenTelemetrySpanImpl.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,21 @@
3939
import io.opencensus.trace.MessageEvent;
4040
import io.opencensus.trace.Span;
4141
import io.opencensus.trace.Status;
42+
import io.opencensus.trace.TraceOptions;
43+
import io.opencensus.trace.Tracestate;
4244
import io.opentelemetry.api.common.AttributeKey;
4345
import io.opentelemetry.api.common.Attributes;
4446
import io.opentelemetry.api.common.AttributesBuilder;
4547
import io.opentelemetry.api.trace.SpanContext;
4648
import io.opentelemetry.api.trace.StatusCode;
4749
import java.util.EnumSet;
4850
import java.util.Map;
49-
import java.util.logging.Logger;
5051

5152
class OpenTelemetrySpanImpl extends Span
5253
implements io.opentelemetry.api.trace.Span, DelegatingSpan {
53-
private static final Logger LOGGER = Logger.getLogger(OpenTelemetrySpanImpl.class.getName());
5454
private static final EnumSet<Span.Options> RECORD_EVENTS_SPAN_OPTIONS =
5555
EnumSet.of(Span.Options.RECORD_EVENTS);
56+
private static final Tracestate OC_TRACESTATE_DEFAULT = Tracestate.builder().build();
5657

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

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

105106
@Override
106107
public void addLink(Link link) {
107-
LOGGER.warning("OpenTelemetry does not support links added after a span is created.");
108+
Preconditions.checkNotNull(link, "link");
109+
AttributesBuilder attributesBuilder = Attributes.builder();
110+
mapAttributes(link.getAttributes(), attributesBuilder);
111+
// DelegatingSpan does not override addLink, so DelegatingSpan.super.addLink(..) would call the
112+
// no-op default method of Span and drop the link. Call the delegate directly instead.
113+
getDelegate()
114+
.addLink(
115+
mapSpanContext(
116+
io.opencensus.trace.SpanContext.create(
117+
link.getTraceId(),
118+
link.getSpanId(),
119+
TraceOptions.DEFAULT,
120+
OC_TRACESTATE_DEFAULT)),
121+
attributesBuilder.build());
108122
}
109123

110124
@Override

opencensus-shim/src/test/java/io/opentelemetry/opencensusshim/OpenTelemetrySpanImplTest.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,27 @@
55

66
package io.opentelemetry.opencensusshim;
77

8+
import static io.opentelemetry.api.common.AttributeKey.stringKey;
89
import static org.mockito.ArgumentMatchers.any;
910
import static org.mockito.ArgumentMatchers.anyString;
1011
import static org.mockito.Mockito.never;
1112
import static org.mockito.Mockito.verify;
1213
import static org.mockito.Mockito.when;
1314

15+
import io.opencensus.trace.AttributeValue;
16+
import io.opencensus.trace.Link;
17+
import io.opencensus.trace.SpanId;
1418
import io.opencensus.trace.Status;
19+
import io.opencensus.trace.TraceId;
20+
import io.opencensus.trace.TraceOptions;
21+
import io.opencensus.trace.Tracestate;
22+
import io.opentelemetry.api.common.Attributes;
1523
import io.opentelemetry.api.trace.Span;
1624
import io.opentelemetry.api.trace.SpanContext;
1725
import io.opentelemetry.api.trace.StatusCode;
26+
import io.opentelemetry.api.trace.TraceFlags;
27+
import io.opentelemetry.api.trace.TraceState;
28+
import java.util.Collections;
1829
import org.junit.jupiter.api.Test;
1930
import org.junit.jupiter.api.extension.ExtendWith;
2031
import org.mockito.Mock;
@@ -26,6 +37,9 @@
2637
@MockitoSettings(strictness = Strictness.LENIENT)
2738
class OpenTelemetrySpanImplTest {
2839

40+
private static final String TRACE_ID = "0123456789abcdef0123456789abcdef";
41+
private static final String SPAN_ID = "fedcba9876543210";
42+
2943
@Mock private Span otelSpan;
3044

3145
private OpenTelemetrySpanImpl shimSpan() {
@@ -47,4 +61,35 @@ void setStatus_withoutDescription_setsCodeOnly() {
4761
verify(otelSpan).setStatus(StatusCode.OK);
4862
verify(otelSpan, never()).setStatus(any(StatusCode.class), anyString());
4963
}
64+
65+
@Test
66+
void addLink_delegatesToOtelSpan() {
67+
shimSpan().addLink(Link.fromSpanContext(ocSpanContext(), Link.Type.CHILD_LINKED_SPAN));
68+
69+
verify(otelSpan).addLink(expectedOtelSpanContext(), Attributes.empty());
70+
}
71+
72+
@Test
73+
void addLink_mapsAttributes() {
74+
shimSpan()
75+
.addLink(
76+
Link.fromSpanContext(
77+
ocSpanContext(),
78+
Link.Type.PARENT_LINKED_SPAN,
79+
Collections.singletonMap("key", AttributeValue.stringAttributeValue("value"))));
80+
81+
verify(otelSpan).addLink(expectedOtelSpanContext(), Attributes.of(stringKey("key"), "value"));
82+
}
83+
84+
private static io.opencensus.trace.SpanContext ocSpanContext() {
85+
return io.opencensus.trace.SpanContext.create(
86+
TraceId.fromLowerBase16(TRACE_ID),
87+
SpanId.fromLowerBase16(SPAN_ID),
88+
TraceOptions.DEFAULT,
89+
Tracestate.builder().build());
90+
}
91+
92+
private static SpanContext expectedOtelSpanContext() {
93+
return SpanContext.create(TRACE_ID, SPAN_ID, TraceFlags.getDefault(), TraceState.getDefault());
94+
}
5095
}

0 commit comments

Comments
 (0)