Skip to content

Commit 63ee502

Browse files
mariusoeMarius Brill
andauthored
Ability to use 64 bit trace IDs (#1432)
Co-authored-by: Marius Brill <marius.brill@novatec-gmbh.de>
1 parent cde2835 commit 63ee502

File tree

9 files changed

+233
-7
lines changed

9 files changed

+233
-7
lines changed

inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/tracing/TracingSettings.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,9 @@ public enum AddCommonTags {
7171
*/
7272
@Positive
7373
private long scheduleDelayMillis = 5000;
74+
75+
/**
76+
* I enabled 64 Bit Trace Ids are used instead of the default 128 Bit.
77+
*/
78+
private boolean use64BitTraceIds = false;
7479
}

inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/basics.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ inspectit:
3838
auto-tracing:
3939
frequency: 50ms
4040
shutdown-delay: 30s
41+
# whether the agent should use 64 bit trace ids instead of 128 bit trace ids
42+
use-64-bit-trace-ids: false
4143
# settings regarding log correlation
4244
log-correlation:
4345
trace-id-mdc-injection:

inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/context/ContextPropagationUtil.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
import io.opencensus.trace.SpanContext;
55
import io.opencensus.trace.Tracing;
66
import io.opencensus.trace.propagation.TextFormat;
7-
import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator;
87
import lombok.extern.slf4j.Slf4j;
98
import org.springframework.util.CollectionUtils;
109
import rocks.inspectit.ocelot.config.model.tracing.PropagationFormat;
1110
import rocks.inspectit.ocelot.core.instrumentation.context.propagation.DatadogFormat;
11+
import rocks.inspectit.ocelot.core.opentelemetry.trace.CustomIdGenerator;
1212

1313
import java.net.URLDecoder;
1414
import java.net.URLEncoder;
@@ -118,12 +118,26 @@ public static Map<String, String> buildPropagationHeaderMap(Stream<Map.Entry<Str
118118
public static Map<String, String> buildPropagationHeaderMap(Stream<Map.Entry<String, Object>> dataToPropagate, SpanContext spanToPropagate) {
119119
String contextCorrelationData = buildCorrelationContextHeader(dataToPropagate);
120120
HashMap<String, String> result = new HashMap<>();
121-
if (contextCorrelationData.length() > 0) {
122-
result.put(CORRELATION_CONTEXT_HEADER, contextCorrelationData);
123-
}
124121
if (spanToPropagate != null) {
125122
propagationFormat.inject(spanToPropagate, result, MAP_INJECTOR);
123+
124+
if (CustomIdGenerator.isUsing64Bit()) {
125+
String traceid = spanToPropagate.getTraceId().toLowerBase16();
126+
// do nothing in case trace ID is already 64bit
127+
if (traceid.length() > 16) {
128+
for (Map.Entry<String, String> entry : result.entrySet()) {
129+
// we only trim the value in case it contains only the trace id (which is not the case when using the w3c trace context format)
130+
if (entry.getValue().equals(traceid)) {
131+
entry.setValue(traceid.substring(16));
132+
}
133+
}
134+
}
135+
}
126136
}
137+
if (contextCorrelationData.length() > 0) {
138+
result.put(CORRELATION_CONTEXT_HEADER, contextCorrelationData);
139+
}
140+
127141
return result;
128142
}
129143

@@ -320,7 +334,7 @@ public static void setPropagationFormat(PropagationFormat format) {
320334
log.info("Using Datadog format for context propagation.");
321335
propagationFormat = DatadogFormat.INSTANCE;
322336
break;
323-
default:
337+
default:
324338
log.warn("The specified propagation format {} is not supported. Falling back to B3 format.", format);
325339
propagationFormat = Tracing.getPropagationComponent().getB3Format();
326340
}

inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImpl.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import rocks.inspectit.ocelot.core.config.InspectitConfigChangedEvent;
3030
import rocks.inspectit.ocelot.core.config.InspectitEnvironment;
3131
import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableMetricsExporterService;
32+
import rocks.inspectit.ocelot.core.opentelemetry.trace.CustomIdGenerator;
3233
import rocks.inspectit.ocelot.core.utils.OpenCensusShimUtils;
3334
import rocks.inspectit.ocelot.core.utils.OpenTelemetryUtils;
3435

@@ -118,8 +119,13 @@ public class OpenTelemetryControllerImpl implements IOpenTelemetryController {
118119
private SdkTracerProvider tracerProvider;
119120

120121
@Autowired
122+
@VisibleForTesting
121123
InspectitEnvironment env;
122124

125+
@Autowired
126+
@VisibleForTesting
127+
CustomIdGenerator idGenerator;
128+
123129
/**
124130
* The {@link DynamicSampler} used for tracing
125131
*/
@@ -310,7 +316,8 @@ void initOtel(InspectitConfig configuration) {
310316
// if any OpenTelemetry has already been registered to GlobalOpenTelemetry, reset it.
311317
if (null != OpenTelemetryUtils.getGlobalOpenTelemetry()) {
312318
// we need to reset it before we can register our custom OpenTelemetryImpl, as GlobalOpenTelemetry is throwing an exception if we want to register a new OpenTelemetry if a previous one is still registered.
313-
log.info("Reset previously registered GlobalOpenTelemetry ({}) during the initialization of {} to register {}", GlobalOpenTelemetry.get()
319+
log.info("Reset previously registered GlobalOpenTelemetry ({}) during the initialization of {} to register {}", GlobalOpenTelemetry
320+
.get()
314321
.getClass()
315322
.getName(), getName(), openTelemetry.getClass().getSimpleName());
316323
GlobalOpenTelemetry.resetForTest();
@@ -339,7 +346,9 @@ private SdkTracerProvider buildTracerProvider(InspectitConfig configuration) {
339346
SdkTracerProviderBuilder builder = SdkTracerProvider.builder()
340347
.setSampler(sampler)
341348
.setResource(serviceNameResource)
342-
.addSpanProcessor(spanProcessor);
349+
.addSpanProcessor(spanProcessor)
350+
.setIdGenerator(idGenerator);
351+
343352
return builder.build();
344353
}
345354

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package rocks.inspectit.ocelot.core.opentelemetry.trace;
2+
3+
import com.google.common.annotations.VisibleForTesting;
4+
import io.opentelemetry.api.trace.SpanId;
5+
import io.opentelemetry.api.trace.TraceId;
6+
import io.opentelemetry.sdk.internal.RandomSupplier;
7+
import io.opentelemetry.sdk.trace.IdGenerator;
8+
import lombok.extern.slf4j.Slf4j;
9+
import org.springframework.beans.factory.annotation.Autowired;
10+
import org.springframework.context.event.EventListener;
11+
import org.springframework.stereotype.Component;
12+
import rocks.inspectit.ocelot.core.config.InspectitConfigChangedEvent;
13+
import rocks.inspectit.ocelot.core.config.InspectitEnvironment;
14+
15+
import javax.annotation.PostConstruct;
16+
import java.util.Random;
17+
import java.util.function.Supplier;
18+
19+
/**
20+
* Trace ID generator for generating 64 bit trace IDs. This class is based on the {@link io.opentelemetry.sdk.trace.RandomIdGenerator}.
21+
*/
22+
@Slf4j
23+
@Component
24+
public class CustomIdGenerator implements IdGenerator {
25+
26+
@Autowired
27+
private InspectitEnvironment env;
28+
29+
private static final long INVALID_ID = 0;
30+
31+
private static final Supplier<Random> randomSupplier = RandomSupplier.platformDefault();
32+
33+
private static boolean using64Bit = false;
34+
35+
public static boolean isUsing64Bit() {
36+
return using64Bit;
37+
}
38+
39+
@PostConstruct
40+
public void initialize() {
41+
setMode(env.getCurrentConfig().getTracing().isUse64BitTraceIds());
42+
}
43+
44+
@EventListener
45+
private void configEventListener(InspectitConfigChangedEvent event) {
46+
boolean oldMode = event.getOldConfig().getTracing().isUse64BitTraceIds();
47+
boolean newMode = event.getNewConfig().getTracing().isUse64BitTraceIds();
48+
49+
if (oldMode != newMode) {
50+
setMode(newMode);
51+
}
52+
}
53+
54+
@VisibleForTesting
55+
void setMode(boolean is64Bit) {
56+
using64Bit = is64Bit;
57+
58+
if (using64Bit) {
59+
log.info("Use of trace IDs with a length of 64 bits.");
60+
} else {
61+
log.info("Use of trace IDs with the default length (128 bits).");
62+
}
63+
}
64+
65+
@Override
66+
public String generateSpanId() {
67+
Random random = randomSupplier.get();
68+
long id;
69+
do {
70+
id = random.nextLong();
71+
} while (id == INVALID_ID);
72+
73+
return SpanId.fromLong(id);
74+
}
75+
76+
@Override
77+
public String generateTraceId() {
78+
if (using64Bit) {
79+
return generate64BitId();
80+
} else {
81+
return generate128BitId();
82+
}
83+
}
84+
85+
private String generate64BitId() {
86+
Random random = randomSupplier.get();
87+
long id;
88+
do {
89+
id = random.nextLong();
90+
} while (id == INVALID_ID);
91+
return TraceId.fromLongs(0L, id);
92+
}
93+
94+
private String generate128BitId() {
95+
Random random = randomSupplier.get();
96+
long idHi = random.nextLong();
97+
long idLo;
98+
do {
99+
idLo = random.nextLong();
100+
} while (idLo == INVALID_ID);
101+
return TraceId.fromLongs(idHi, idLo);
102+
}
103+
104+
@Override
105+
public String toString() {
106+
return "RandomIdGenerator64Bit{}";
107+
}
108+
}

inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplIntTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818
import org.junit.jupiter.api.BeforeAll;
1919
import org.junit.jupiter.api.Test;
2020
import org.junit.jupiter.api.extension.RegisterExtension;
21+
import org.mockito.Mock;
2122
import org.springframework.beans.factory.annotation.Autowired;
2223
import rocks.inspectit.ocelot.config.model.exporters.ExporterEnabledState;
2324
import rocks.inspectit.ocelot.core.SLF4JBridgeHandlerUtils;
2425
import rocks.inspectit.ocelot.core.SpringTestBase;
2526
import rocks.inspectit.ocelot.core.exporter.LoggingTraceExporterService;
27+
import rocks.inspectit.ocelot.core.opentelemetry.trace.CustomIdGenerator;
2628
import rocks.inspectit.ocelot.core.utils.OpenTelemetryUtils;
2729

2830
import java.io.IOException;

inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/opentelemetry/OpenTelemetryControllerImplTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import rocks.inspectit.ocelot.config.model.InspectitConfig;
2525
import rocks.inspectit.ocelot.core.config.InspectitEnvironment;
2626
import rocks.inspectit.ocelot.core.exporter.DynamicallyActivatableMetricsExporterService;
27+
import rocks.inspectit.ocelot.core.opentelemetry.trace.CustomIdGenerator;
2728
import rocks.inspectit.ocelot.core.service.DynamicallyActivatableService;
2829

2930
import java.lang.reflect.Method;
@@ -45,11 +46,15 @@ class OpenTelemetryControllerImplTest {
4546
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
4647
InspectitEnvironment env;
4748

49+
@Mock
50+
CustomIdGenerator idGenerator;
51+
4852
@BeforeEach
4953
void initOpenTelemetryController() {
5054
// mock max-export-batch-size to avoid exceptions
5155
when(env.getCurrentConfig().getTracing().getMaxExportBatchSize()).thenReturn(512);
5256
openTelemetryController.env = env;
57+
openTelemetryController.idGenerator = idGenerator;
5358
openTelemetryController.init();
5459
openTelemetryController.start();
5560
clearInvocations(openTelemetryController);
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package rocks.inspectit.ocelot.core.opentelemetry.trace;
2+
3+
import org.junit.jupiter.api.Nested;
4+
import org.junit.jupiter.api.Test;
5+
import org.junit.jupiter.api.extension.ExtendWith;
6+
import org.mockito.InjectMocks;
7+
import org.mockito.junit.jupiter.MockitoExtension;
8+
9+
import java.nio.charset.StandardCharsets;
10+
11+
import static org.assertj.core.api.Assertions.assertThat;
12+
13+
@ExtendWith(MockitoExtension.class)
14+
public class CustomIdGeneratorTest {
15+
16+
int getBitSize(String string) {
17+
return string.getBytes(StandardCharsets.UTF_8).length * 8;
18+
}
19+
20+
@InjectMocks
21+
CustomIdGenerator idGenerator;
22+
23+
@Nested
24+
class TestGenerateSpanId {
25+
26+
@Test
27+
public void is64Bit() {
28+
String id = idGenerator.generateSpanId();
29+
assertThat(getBitSize(id)).isEqualTo(128);
30+
}
31+
}
32+
33+
@Nested
34+
class TestGenerateTraceId {
35+
36+
@Test
37+
public void is64Bit() {
38+
idGenerator.setMode(true);
39+
40+
String id = idGenerator.generateTraceId();
41+
42+
assertThat(id.substring(0,16)).isEqualTo("0000000000000000");
43+
assertThat(getBitSize(id)).isEqualTo(256);
44+
}
45+
46+
47+
@Test
48+
public void is128Bit() {
49+
idGenerator.setMode(false);
50+
51+
String id = idGenerator.generateTraceId();
52+
53+
assertThat(id.substring(0,16)).isNotEqualTo("0000000000000000");
54+
assertThat(getBitSize(id)).isEqualTo(256);
55+
}
56+
57+
@Test
58+
public void defaultMode() {
59+
String id = idGenerator.generateTraceId();
60+
61+
assertThat(id.substring(0,16)).isNotEqualTo("0000000000000000");
62+
assertThat(getBitSize(id)).isEqualTo(256);
63+
}
64+
}
65+
66+
}

inspectit-ocelot-documentation/docs/tracing/tracing.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,19 @@ Currently the following formats are supported for sending correlation informatio
6868

6969
:::important
7070
It is important to note that this configuration refers to the format of the correlation information used to **send this data**. When processing correlation information that the agent receives, it automatically uses the correct format.
71+
:::
72+
73+
### Using 64-Bit Trace IDs
74+
75+
Since version 2.0.0, the inspectIT Ocelot Agent is able to generate trace IDs with a size of 64 bits instead of the 128 bit trace IDs used by default by the agent.
76+
The functionality that trace IDs with a length of 64 bits are generated can be activated with the following configuration:
77+
78+
```YAML
79+
inspectit:
80+
tracing:
81+
use-64-bit-trace-ids: true
82+
```
83+
84+
:::important
85+
Please note that some propagation formats do not support 64-bit Ids, such as the W3C "Trace Context". In this case the 64-bit trace IDs are padded with leading zeros.
7186
:::

0 commit comments

Comments
 (0)