Skip to content
This repository was archived by the owner on Aug 6, 2024. It is now read-only.

Commit 02bb7b9

Browse files
New sleuth api for wavefront (#105)
* Refactored the span handler to use Sleuth's API * Abstracted stuff - will need to move all of Sleuth stuff back to Sleuth * Bumped version of cloud train * Polish
1 parent 94f828d commit 02bb7b9

File tree

5 files changed

+172
-59
lines changed

5 files changed

+172
-59
lines changed

wavefront-spring-boot-parent/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<properties>
2121
<java.version>1.8</java.version>
2222
<spring-boot.version>2.4.2</spring-boot.version>
23-
<spring-cloud.version>2020.0.0</spring-cloud.version>
23+
<spring-cloud.version>2020.0.2-SNAPSHOT</spring-cloud.version>
2424
</properties>
2525

2626
<dependencyManagement>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.wavefront.spring.autoconfigure;
2+
3+
import java.io.Closeable;
4+
import java.io.IOException;
5+
6+
import brave.handler.MutableSpan;
7+
import brave.handler.SpanHandler;
8+
import brave.propagation.TraceContext;
9+
10+
import org.springframework.cloud.sleuth.brave.bridge.BraveFinishedSpan;
11+
import org.springframework.cloud.sleuth.brave.bridge.BraveTraceContext;
12+
13+
class WavefrontSleuthBraveSpanHandler extends SpanHandler implements Runnable, Closeable {
14+
15+
final WavefrontSleuthSpanHandler spanHandler;
16+
17+
WavefrontSleuthBraveSpanHandler(WavefrontSleuthSpanHandler spanHandler) {
18+
this.spanHandler = spanHandler;
19+
}
20+
21+
@Override
22+
public boolean end(TraceContext context, MutableSpan span, Cause cause) {
23+
return spanHandler.end(BraveTraceContext.fromBrave(context), BraveFinishedSpan.fromBrave(span));
24+
}
25+
26+
@Override
27+
public void close() throws IOException {
28+
this.spanHandler.close();
29+
}
30+
31+
@Override
32+
public void run() {
33+
this.spanHandler.run();
34+
}
35+
}

wavefront-spring-boot/src/main/java/com/wavefront/spring/autoconfigure/WavefrontSleuthSpanHandler.java

Lines changed: 102 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
import java.io.Closeable;
44
import java.io.IOException;
55
import java.util.ArrayList;
6+
import java.util.Arrays;
67
import java.util.Collections;
78
import java.util.HashSet;
89
import java.util.List;
910
import java.util.Locale;
1011
import java.util.Map;
11-
import java.util.Objects;
1212
import java.util.Set;
1313
import java.util.UUID;
1414
import java.util.concurrent.Executors;
@@ -17,10 +17,8 @@
1717
import java.util.concurrent.TimeUnit;
1818
import java.util.stream.Collectors;
1919

20-
import brave.handler.MutableSpan;
21-
import brave.handler.SpanHandler;
22-
import brave.propagation.TraceContext;
2320
import com.wavefront.internal.reporter.WavefrontInternalReporter;
21+
import com.wavefront.java_sdk.com.google.common.collect.Iterators;
2422
import com.wavefront.java_sdk.com.google.common.collect.Sets;
2523
import com.wavefront.sdk.common.NamedThreadFactory;
2624
import com.wavefront.sdk.common.Pair;
@@ -32,6 +30,10 @@
3230
import org.apache.commons.logging.Log;
3331
import org.apache.commons.logging.LogFactory;
3432

33+
import org.springframework.cloud.sleuth.TraceContext;
34+
import org.springframework.cloud.sleuth.exporter.FinishedSpan;
35+
import org.springframework.util.StringUtils;
36+
3537
import static com.wavefront.internal.SpanDerivedMetricsUtils.TRACING_DERIVED_PREFIX;
3638
import static com.wavefront.internal.SpanDerivedMetricsUtils.reportHeartbeats;
3739
import static com.wavefront.internal.SpanDerivedMetricsUtils.reportWavefrontGeneratedData;
@@ -66,7 +68,7 @@
6668
* {@link UUID#timestamp()} on UUIDs converted here, or in other Wavefront code, as it might
6769
* throw.
6870
*/
69-
final class WavefrontSleuthSpanHandler extends SpanHandler implements Runnable, Closeable {
71+
public final class WavefrontSleuthSpanHandler implements Runnable, Closeable {
7072
private static final Log LOG = LogFactory.getLog(WavefrontSleuthSpanHandler.class);
7173

7274
// https://github.com/wavefrontHQ/wavefront-proxy/blob/3dd1fa11711a04de2d9d418e2269f0f9fb464f36/proxy/src/main/java/com/wavefront/agent/listeners/tracing/ZipkinPortUnificationHandler.java#L114-L114
@@ -75,7 +77,21 @@ final class WavefrontSleuthSpanHandler extends SpanHandler implements Runnable,
7577
private final static String DEFAULT_SOURCE = "wavefront-spring-boot";
7678
private final static String WAVEFRONT_GENERATED_COMPONENT = "wavefront-generated";
7779

78-
final LinkedBlockingQueue<Pair<TraceContext, MutableSpan>> spanBuffer;
80+
private static final int LONG_BYTES = Long.SIZE / Byte.SIZE;
81+
82+
private static final int BYTE_BASE16 = 2;
83+
84+
private static final int LONG_BASE16 = BYTE_BASE16 * LONG_BYTES;
85+
86+
private static final int TRACE_ID_HEX_SIZE = 2 * LONG_BASE16;
87+
88+
private static final String ALPHABET = "0123456789abcdef";
89+
90+
private static final int ASCII_CHARACTERS = 128;
91+
92+
private static final byte[] DECODING = buildDecodingArray();
93+
94+
final LinkedBlockingQueue<Pair<TraceContext, FinishedSpan>> spanBuffer;
7995
final WavefrontSender wavefrontSender;
8096
final WavefrontInternalReporter wfInternalReporter;
8197
final Set<String> traceDerivedCustomTagKeys;
@@ -144,7 +160,7 @@ final class WavefrontSleuthSpanHandler extends SpanHandler implements Runnable,
144160

145161
// Exact same behavior as WavefrontSpanReporter
146162
// https://github.com/wavefrontHQ/wavefront-opentracing-sdk-java/blob/f1f08d8daf7b692b9b61dcd5bc24ca6befa8e710/src/main/java/com/wavefront/opentracing/reporting/WavefrontSpanReporter.java#L163-L179
147-
@Override public boolean end(TraceContext context, MutableSpan span, Cause cause) {
163+
public boolean end(TraceContext context, FinishedSpan span) {
148164
spansReceived.increment();
149165
if (!spanBuffer.offer(Pair.of(context, span))) {
150166
spansDropped.increment();
@@ -160,30 +176,48 @@ List<Pair<String, String>> getDefaultTags() {
160176
return Collections.unmodifiableList(this.defaultTags);
161177
}
162178

163-
private void send(TraceContext context, MutableSpan span) {
164-
UUID traceId = new UUID(context.traceIdHigh(), context.traceId());
165-
UUID spanId = new UUID(0L, context.spanId());
179+
private String padLeftWithZeros(String string, int length) {
180+
if (string.length() >= length) {
181+
return string;
182+
}
183+
else {
184+
StringBuilder sb = new StringBuilder(length);
185+
for (int i = string.length(); i < length; i++) {
186+
sb.append('0');
187+
}
188+
189+
return sb.append(string).toString();
190+
}
191+
}
192+
193+
private void send(TraceContext context, FinishedSpan span) {
194+
String traceIdString = padLeftWithZeros(context.traceId(), TRACE_ID_HEX_SIZE);
195+
String traceIdHigh = traceIdString.substring(0, traceIdString.length() / 2);
196+
String traceIdLow = traceIdString.substring(traceIdString.length() / 2);
197+
UUID traceId = new UUID(longFromBase16String(traceIdHigh), longFromBase16String(traceIdLow));
198+
UUID spanId = new UUID(0L, longFromBase16String(context.spanId()));
166199

167200
// NOTE: wavefront-opentracing-sdk-java and wavefront-proxy differ, but we prefer the former.
168201
// https://github.com/wavefrontHQ/wavefront-opentracing-sdk-java/blob/f1f08d8daf7b692b9b61dcd5bc24ca6befa8e710/src/main/java/com/wavefront/opentracing/reporting/WavefrontSpanReporter.java#L187-L190
169202
// https://github.com/wavefrontHQ/wavefront-proxy/blob/3dd1fa11711a04de2d9d418e2269f0f9fb464f36/proxy/src/main/java/com/wavefront/agent/listeners/tracing/ZipkinPortUnificationHandler.java#L248-L252
170203
List<UUID> parents = null;
171-
if (context.parentIdAsLong() != 0L) {
172-
parents = Collections.singletonList(new UUID(0L, context.parentIdAsLong()));
204+
String parentId = context.parentId();
205+
if (StringUtils.hasText(parentId) && longFromBase16String(parentId) != 0L) {
206+
parents = Collections.singletonList(new UUID(0L, longFromBase16String(parentId)));
173207
}
174208
List<UUID> followsFrom = null;
175209

176210
// https://github.com/wavefrontHQ/wavefront-proxy/blob/3dd1fa11711a04de2d9d418e2269f0f9fb464f36/proxy/src/main/java/com/wavefront/agent/listeners/tracing/ZipkinPortUnificationHandler.java#L344-L345
177-
String name = span.name();
211+
String name = span.getName();
178212
if (name == null) name = DEFAULT_SPAN_NAME;
179213

180214
// Start and duration become 0L if unset. Any positive duration rounds up to 1 millis.
181-
long startMillis = span.startTimestamp() / 1000L, finishMillis = span.finishTimestamp() / 1000L;
182-
long durationMicros = span.finishTimestamp() - span.startTimestamp();
215+
long startMillis = span.getStartTimestamp() / 1000L, finishMillis = span.getEndTimestamp() / 1000L;
216+
long durationMicros = span.getEndTimestamp() - span.getStartTimestamp();
183217
long durationMillis = startMillis != 0 && finishMillis != 0L ? Math.max(finishMillis - startMillis, 1L) : 0L;
184218

185219
List<SpanLog> spanLogs = convertAnnotationsToSpanLogs(span);
186-
TagList tags = new TagList(defaultTagKeys, defaultTags, context, span);
220+
TagList tags = new TagList(defaultTagKeys, defaultTags, span);
187221

188222
try {
189223
wavefrontSender.sendSpan(name, startMillis, durationMillis, source, traceId, spanId,
@@ -212,6 +246,38 @@ private void send(TraceContext context, MutableSpan span) {
212246
}
213247
}
214248

249+
private static byte[] buildDecodingArray() {
250+
byte[] decoding = new byte[ASCII_CHARACTERS];
251+
Arrays.fill(decoding, (byte) -1);
252+
for (int i = 0; i < ALPHABET.length(); i++) {
253+
char c = ALPHABET.charAt(i);
254+
decoding[c] = (byte) i;
255+
}
256+
return decoding;
257+
}
258+
259+
/**
260+
* Returns the {@code long} value whose base16 representation is stored in the first
261+
* 16 chars of {@code chars} starting from the {@code offset}.
262+
* @param chars the base16 representation of the {@code long}.
263+
*/
264+
private static long longFromBase16String(CharSequence chars) {
265+
int offset = 0;
266+
return (decodeByte(chars.charAt(offset), chars.charAt(offset + 1)) & 0xFFL) << 56
267+
| (decodeByte(chars.charAt(offset + 2), chars.charAt(offset + 3)) & 0xFFL) << 48
268+
| (decodeByte(chars.charAt(offset + 4), chars.charAt(offset + 5)) & 0xFFL) << 40
269+
| (decodeByte(chars.charAt(offset + 6), chars.charAt(offset + 7)) & 0xFFL) << 32
270+
| (decodeByte(chars.charAt(offset + 8), chars.charAt(offset + 9)) & 0xFFL) << 24
271+
| (decodeByte(chars.charAt(offset + 10), chars.charAt(offset + 11)) & 0xFFL) << 16
272+
| (decodeByte(chars.charAt(offset + 12), chars.charAt(offset + 13)) & 0xFFL) << 8
273+
| (decodeByte(chars.charAt(offset + 14), chars.charAt(offset + 15)) & 0xFFL);
274+
}
275+
276+
private static byte decodeByte(char hi, char lo) {
277+
int decoded = DECODING[hi] << 4 | DECODING[lo];
278+
return (byte) decoded;
279+
}
280+
215281
/**
216282
* Extracted for test isolation and as parsing otherwise implies multiple-returns or scanning
217283
* later.
@@ -225,18 +291,20 @@ static final class TagList extends ArrayList<Pair<String, String>> {
225291

226292
TagList(
227293
Set<String> defaultTagKeys,
228-
List<Pair<String, String>> defaultTags,
229-
TraceContext context,
230-
MutableSpan span
294+
List<Pair<String, String>> defaultTags,FinishedSpan span
231295
){
232-
super(defaultTags.size() + span.tagCount());
233-
boolean debug = context.debug(), hasAnnotations = span.annotationCount() > 0;
234-
isError = span.error() != null;
296+
super(defaultTags.size() + span.getTags().size());
297+
// TODO: OTel doesn't have a notion of debug
298+
boolean debug = false;
299+
boolean hasAnnotations = span.getEvents().size() > 0;
300+
isError = span.getError() != null;
235301

236-
int tagCount = span.tagCount();
302+
int tagCount = span.getTags().size();
237303
addAll(defaultTags);
238304
for (int i = 0; i < tagCount; i++) {
239-
String key = span.tagKeyAt(i), value = span.tagValueAt(i);
305+
String tagKey = Iterators.get(span.getTags().keySet().iterator(), i);
306+
String tagValue = Iterators.get(span.getTags().values().iterator(), i);
307+
String key = tagKey, value = tagValue;
240308
String lcKey = key.toLowerCase(Locale.ROOT);
241309
if (lcKey.equals(ERROR_TAG_KEY)) {
242310
isError = true;
@@ -261,8 +329,8 @@ static final class TagList extends ArrayList<Pair<String, String>> {
261329
if (debug) add(Pair.of(DEBUG_TAG_KEY, "true"));
262330

263331
// https://github.com/wavefrontHQ/wavefront-proxy/blob/3dd1fa11711a04de2d9d418e2269f0f9fb464f36/proxy/src/main/java/com/wavefront/agent/listeners/tracing/ZipkinPortUnificationHandler.java#L254-L266
264-
if (span.kind() != null) {
265-
String kind = span.kind().toString().toLowerCase();
332+
if (span.getKind() != null) {
333+
String kind = span.getKind().toString().toLowerCase();
266334
add(Pair.of("span.kind", kind));
267335
if (hasAnnotations) {
268336
add(Pair.of("_spanSecondaryId", kind));
@@ -273,20 +341,21 @@ static final class TagList extends ArrayList<Pair<String, String>> {
273341
if (hasAnnotations) add(Pair.of(SPAN_LOG_KEY, "true"));
274342

275343
// https://github.com/wavefrontHQ/wavefront-proxy/blob/3dd1fa11711a04de2d9d418e2269f0f9fb464f36/proxy/src/main/java/com/wavefront/agent/listeners/tracing/ZipkinPortUnificationHandler.java#L324-L327
276-
if (span.localIp() != null) {
277-
add(Pair.of("ipv4", span.localIp())); // NOTE: this could be IPv6!!
344+
if (span.getLocalIp() != null) {
345+
add(Pair.of("ipv4", span.getLocalIp())); // NOTE: this could be IPv6!!
278346
}
279347
}
280348
}
281349

282350
// https://github.com/wavefrontHQ/wavefront-proxy/blob/3dd1fa11711a04de2d9d418e2269f0f9fb464f36/proxy/src/main/java/com/wavefront/agent/listeners/tracing/ZipkinPortUnificationHandler.java#L397-L402
283-
static List<SpanLog> convertAnnotationsToSpanLogs(MutableSpan span) {
284-
int annotationCount = span.annotationCount();
351+
static List<SpanLog> convertAnnotationsToSpanLogs(FinishedSpan span) {
352+
int annotationCount = span.getEvents().size();
285353
if (annotationCount == 0) return Collections.emptyList();
286354
List<SpanLog> spanLogs = new ArrayList<>(annotationCount);
287355
for (int i = 0; i < annotationCount; i++) {
288-
long epochMicros = span.annotationTimestampAt(i);
289-
String value = span.annotationValueAt(i);
356+
Map.Entry<Long, String> entry = Iterators.get(span.getEvents().iterator(), i);
357+
long epochMicros = entry.getKey();
358+
String value = entry.getValue();
290359
spanLogs.add(new SpanLog(epochMicros, Collections.singletonMap("annotation", value)));
291360
}
292361
return spanLogs;
@@ -295,7 +364,7 @@ static List<SpanLog> convertAnnotationsToSpanLogs(MutableSpan span) {
295364
@Override public void run() {
296365
while (!stop) {
297366
try {
298-
Pair<TraceContext, MutableSpan> contextAndSpan = spanBuffer.take();
367+
Pair<TraceContext, FinishedSpan> contextAndSpan = spanBuffer.take();
299368
send(contextAndSpan._1, contextAndSpan._2);
300369
} catch (InterruptedException ex) {
301370
if (LOG.isInfoEnabled()) {
Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.wavefront.spring.autoconfigure;
22

3+
import brave.Tracer;
34
import brave.TracingCustomizer;
45
import brave.handler.SpanHandler;
56
import com.wavefront.sdk.common.WavefrontSender;
@@ -23,30 +24,37 @@
2324
* @author Stephane Nicoll
2425
*/
2526
@Configuration(proxyBeanMethods = false)
26-
@ConditionalOnClass({ SpanNamer.class, TracingCustomizer.class, SpanHandler.class })
27+
@ConditionalOnClass({ SpanNamer.class, MeterRegistry.class, WavefrontConfig.class, WavefrontSender.class })
2728
@AutoConfigureBefore(BraveAutoConfiguration.class)
2829
class WavefrontTracingSleuthConfiguration {
2930

3031
static final String BEAN_NAME = "wavefrontTracingCustomizer";
3132

32-
@Bean(BEAN_NAME)
33-
@ConditionalOnMissingBean(name = BEAN_NAME)
33+
@Bean
3434
@ConditionalOnBean({ MeterRegistry.class, WavefrontConfig.class, WavefrontSender.class })
35-
TracingCustomizer wavefrontTracingCustomizer(MeterRegistry meterRegistry,
36-
WavefrontSender wavefrontSender,
37-
ApplicationTags applicationTags,
38-
WavefrontConfig wavefrontConfig,
39-
WavefrontProperties wavefrontProperties) {
40-
WavefrontSleuthSpanHandler spanHandler = new WavefrontSleuthSpanHandler(
41-
// https://github.com/wavefrontHQ/wavefront-opentracing-sdk-java/blob/f1f08d8daf7b692b9b61dcd5bc24ca6befa8e710/src/main/java/com/wavefront/opentracing/reporting/WavefrontSpanReporter.java#L54
42-
50000, // TODO: maxQueueSize should be a property, ya?
43-
wavefrontSender,
44-
meterRegistry,
45-
wavefrontConfig.source(),
46-
applicationTags,
47-
wavefrontProperties);
48-
49-
return t -> t.traceId128Bit(true).supportsJoin(false).addSpanHandler(spanHandler);
35+
WavefrontSleuthSpanHandler wavefrontSleuthSpanHandler(MeterRegistry meterRegistry,
36+
WavefrontSender wavefrontSender,
37+
ApplicationTags applicationTags,
38+
WavefrontConfig wavefrontConfig,
39+
WavefrontProperties wavefrontProperties) {
40+
return new WavefrontSleuthSpanHandler(
41+
// https://github.com/wavefrontHQ/wavefront-opentracing-sdk-java/blob/f1f08d8daf7b692b9b61dcd5bc24ca6befa8e710/src/main/java/com/wavefront/opentracing/reporting/WavefrontSpanReporter.java#L54
42+
50000, // TODO: maxQueueSize should be a property, ya?
43+
wavefrontSender,
44+
meterRegistry,
45+
wavefrontConfig.source(),
46+
applicationTags,
47+
wavefrontProperties);
5048
}
5149

50+
@Configuration(proxyBeanMethods = false)
51+
@ConditionalOnClass({Tracer.class, TracingCustomizer.class, SpanHandler.class })
52+
static class BraveCustomizerConfiguration {
53+
@Bean(BEAN_NAME)
54+
@ConditionalOnMissingBean(name = BEAN_NAME)
55+
@ConditionalOnBean({ MeterRegistry.class, WavefrontConfig.class, WavefrontSender.class })
56+
TracingCustomizer wavefrontTracingCustomizer(WavefrontSleuthSpanHandler spanHandler) {
57+
return t -> t.traceId128Bit(true).supportsJoin(false).addSpanHandler(new WavefrontSleuthBraveSpanHandler(spanHandler));
58+
}
59+
}
5260
}

0 commit comments

Comments
 (0)