Skip to content

Commit 03b0c33

Browse files
authored
feat: add trace_id pprof label (#89)
* feat: add trace_id pprof label * perf: parse trace_id hex without allocating substrings * fix: guard setTraceId against malformed trace IDs
1 parent 5a86f91 commit 03b0c33

4 files changed

Lines changed: 38 additions & 5 deletions

File tree

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ link traces with the profiling data, and find specific lines of code related to
77
* Because of how sampling profilers work, spans shorter than the sample interval may not be captured. By default pyroscope CPU profiler probes stack traces 100 times per second, meaning that spans shorter than 10ms may not be captured.
88

99

10-
Java code can be easily instrumented with otel-profiling-java package -
11-
a `OpenTelemetry` implementation, that annotates profiling data with span IDs which makes it possible to filter
12-
out profile of a particular trace span in Pyroscope.
10+
Java code can be easily instrumented with the otel-profiling-java package, an `OpenTelemetry` implementation that annotates profiling data with `trace_id` and `span_id` labels, which makes it possible to filter the profile of a particular trace (or trace span) in Pyroscope.
1311

1412
Visit [docs](https://grafana.com/docs/pyroscope/latest/configure-client/trace-span-profiles/java-span-profiles/) page for usage and configuration documentation.
1513

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
pyroscope_version=2.5.1
1+
pyroscope_version=2.6.0
22
# x-release-please-start-version
33
otel_profiling_version=2.0.6
44
# x-release-please-end

lib/src/main/java/io/otel/pyroscope/PyroscopeOtelSpanProcessor.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ public void onStart(Context parentContext, ReadWriteSpan span) {
6363

6464
span.setAttribute(ATTRIBUTE_KEY_PROFILE_ID, strProfileId);
6565
asprof.setTracingContext(spanId, spanName);
66+
// W3C trace ID is 32 hex chars (128 bits). Parse directly into two longs
67+
// to avoid the String#substring allocations on this hot path.
68+
String traceId = span.getSpanContext().getTraceId();
69+
try {
70+
asprof.setTraceId(parseHex64(traceId, 0), parseHex64(traceId, 16));
71+
} catch (NumberFormatException | IndexOutOfBoundsException e) {
72+
asprof.setTraceId(0, 0);
73+
}
6674
}
6775

6876
@Override
@@ -71,6 +79,7 @@ public void onEnd(ReadableSpan span) {
7179
return;
7280
}
7381
asprof.setTracingContext(0, 0);
82+
asprof.setTraceId(0L, 0L);
7483
}
7584

7685
public static long parseSpanId(String strProfileId) {
@@ -84,6 +93,25 @@ public static long parseSpanId(String strProfileId) {
8493
}
8594
}
8695

96+
static long parseHex64(String s, int offset) {
97+
long result = 0L;
98+
for (int i = 0; i < 16; i++) {
99+
int c = s.charAt(offset + i);
100+
int nibble;
101+
if (c >= '0' && c <= '9') {
102+
nibble = c - '0';
103+
} else if (c >= 'a' && c <= 'f') {
104+
nibble = c - 'a' + 10;
105+
} else if (c >= 'A' && c <= 'F') {
106+
nibble = c - 'A' + 10;
107+
} else {
108+
throw new NumberFormatException("invalid hex char in trace_id at index " + (offset + i));
109+
}
110+
result = (result << 4) | nibble;
111+
}
112+
return result;
113+
}
114+
87115
public static boolean isRootSpan(ReadableSpan span) {
88116
SpanContext parent = span.getParentSpanContext();
89117
boolean noParent = parent == SpanContext.getInvalid();

otel-extension/src/main/java/io/otel/pyroscope/PyroscopeOtelSpanProcessor.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,21 @@ public void onStart(Context parentContext, ReadWriteSpan span) {
5252

5353
span.setAttribute(ATTRIBUTE_KEY_PROFILE_ID, strProfileId);
5454
api.setTracingContext(spanId, spanName);
55+
try {
56+
api.setTraceId(span.getSpanContext().getTraceId());
57+
} catch (NumberFormatException | IndexOutOfBoundsException e) {
58+
api.clearTraceId();
59+
}
5560
}
5661

5762
@Override
5863
public void onEnd(ReadableSpan span) {
5964
if (configuration.rootSpanOnly && !isRootSpan(span)) {
6065
return;
6166
}
62-
getProfiler().setTracingContext(0, 0);
67+
ProfilerApi api = getProfiler();
68+
api.setTracingContext(0, 0);
69+
api.clearTraceId();
6370
}
6471

6572
public static long parseSpanId(String strProfileId) {

0 commit comments

Comments
 (0)