Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
21d4a01
gcp auth
zeitlinger Jul 11, 2025
f9eb67b
./gradlew spotlessApply
otelbot[bot] Jul 11, 2025
35fc448
copy declarative config bridge from instrumentation
zeitlinger Jul 14, 2025
30d61fc
support config bridge
zeitlinger Jul 14, 2025
0f49549
support config bridge
zeitlinger Jul 14, 2025
fc7cd07
copy declarative config bridge from instrumentation
zeitlinger Jul 14, 2025
d786ea6
format
zeitlinger Jul 14, 2025
bf9c8e6
cleanup
zeitlinger Jul 14, 2025
b70486a
cleanup
zeitlinger Jul 14, 2025
4d56209
fix test
zeitlinger Jul 14, 2025
85ec1cb
fix
zeitlinger Jul 14, 2025
83e74a7
add property translation for inferred spans
zeitlinger Jul 14, 2025
9fe79a1
inferred spans
zeitlinger Jul 15, 2025
71f21b7
inferred spans
zeitlinger Jul 15, 2025
0618b39
inferred spans
zeitlinger Jul 15, 2025
27fc125
baggage processor
zeitlinger Jul 15, 2025
d27d7ce
format
zeitlinger Jul 15, 2025
1e77d6f
format
zeitlinger Jul 15, 2025
548c0c4
stack trace span processor
zeitlinger Jul 15, 2025
092fce3
stack trace span processor
zeitlinger Jul 15, 2025
dd10055
fix
zeitlinger Jul 15, 2025
444833f
fix
zeitlinger Jul 15, 2025
4211e85
fix
zeitlinger Jul 15, 2025
e4de567
make temp dir more reliable
zeitlinger Jul 15, 2025
19d017d
make temp dir more reliable
zeitlinger Jul 15, 2025
fb4a7ef
revert inferred spans (flaky)
zeitlinger Jul 15, 2025
a9d9c05
baggage is in a separate PR
zeitlinger Jul 16, 2025
4a119c9
use unified bridge
zeitlinger Jul 18, 2025
4f7014a
add experimental- suffix, add test
zeitlinger Jul 22, 2025
6587b31
add experimental- suffix, add test
zeitlinger Jul 22, 2025
d0718e6
update bridge to match agent
zeitlinger Jul 24, 2025
e042b15
update bridge to match agent
zeitlinger Jul 24, 2025
5bf5e78
Revert "update bridge to match agent"
zeitlinger Jul 24, 2025
b3bfd03
update bridge to match agent
zeitlinger Jul 24, 2025
712cf27
Update gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/…
zeitlinger Aug 4, 2025
d2e2ef4
Update gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/…
zeitlinger Aug 4, 2025
85fad27
pr review
zeitlinger Aug 4, 2025
65d2f8e
pr review
zeitlinger Aug 4, 2025
91be485
pr review
zeitlinger Aug 4, 2025
bfe1c5e
update config bridge from agent
zeitlinger Aug 15, 2025
454fe0d
./gradlew spotlessApply
otelbot[bot] Aug 15, 2025
4b10dda
move bridge to agent
zeitlinger Aug 22, 2025
26cc42d
Merge remote-tracking branch 'origin/main' into span-stacktrace
zeitlinger Sep 15, 2025
17bbdf0
remove
zeitlinger Sep 15, 2025
741de0f
update
zeitlinger Sep 15, 2025
2e4c0bd
readme
zeitlinger Sep 15, 2025
4d82100
./gradlew spotlessApply
otelbot[bot] Sep 15, 2025
0dcce5e
Merge remote-tracking branch 'origin/main' into span-stacktrace
zeitlinger Sep 15, 2025
0dbfb59
fix casing
zeitlinger Sep 17, 2025
d6b0de2
add note about duration
zeitlinger Sep 19, 2025
e72361e
map min duration
zeitlinger Sep 19, 2025
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
16 changes: 15 additions & 1 deletion span-stacktrace/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

# Span stacktrace capture

This module provides a `SpanProcessor` that captures the [`code.stacktrace`](https://opentelemetry.io/docs/specs/semconv/attributes-registry/code/).
Expand All @@ -25,6 +24,21 @@ SDK when included in the application runtime dependencies.
- value is the class name of a class implementing `java.util.function.Predicate<ReadableSpan>`
- filter class must be publicly accessible and provide a no-arg constructor

### Usage with declarative configuration

You can enable the stacktrace span processor using declarative YAML configuration with the OpenTelemetry SDK. For example:

```yaml
file_format: 1.0-rc.1
tracer_provider:
processors:
- experimental_stacktrace:
min_duration: 10 # minimal duration in ms, default is 5, MUST be an integer
filter: my.class.Name # optional, default is to include all spans
```

This configuration will register the StackTraceSpanProcessor for all spans.

## Component owners

- [Jack Shirazi](https://github.com/jackshirazi), Elastic
Expand Down
5 changes: 5 additions & 0 deletions span-stacktrace/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,13 @@ dependencies {

compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi")
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator")
compileOnly("io.opentelemetry.instrumentation:opentelemetry-declarative-config-bridge")
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi")
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator")
testImplementation("io.opentelemetry.instrumentation:opentelemetry-declarative-config-bridge")

compileOnly("io.opentelemetry.semconv:opentelemetry-semconv")
testImplementation("io.opentelemetry.semconv:opentelemetry-semconv")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,26 @@ public class StackTraceAutoConfig implements AutoConfigurationCustomizerProvider

private static final Logger log = Logger.getLogger(StackTraceAutoConfig.class.getName());

private static final String CONFIG_MIN_DURATION =
"otel.java.experimental.span-stacktrace.min.duration";
static final String PREFIX = "otel.java.experimental.span-stacktrace.";
static final String CONFIG_MIN_DURATION = PREFIX + "min.duration";
private static final Duration CONFIG_MIN_DURATION_DEFAULT = Duration.ofMillis(5);

private static final String CONFIG_FILTER = "otel.java.experimental.span-stacktrace.filter";
private static final String CONFIG_FILTER = PREFIX + "filter";

@Override
public void customize(AutoConfigurationCustomizer config) {
config.addTracerProviderCustomizer(
(providerBuilder, properties) -> {
long minDuration = getMinDuration(properties);
if (minDuration >= 0) {
Predicate<ReadableSpan> filter = getFilterPredicate(properties);
providerBuilder.addSpanProcessor(new StackTraceSpanProcessor(minDuration, filter));
if (getMinDuration(properties) >= 0) {
providerBuilder.addSpanProcessor(create(properties));
}
return providerBuilder;
});
}

static StackTraceSpanProcessor create(ConfigProperties properties) {
return new StackTraceSpanProcessor(getMinDuration(properties), getFilterPredicate(properties));
}

// package-private for testing
static long getMinDuration(ConfigProperties properties) {
long minDuration =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.stacktrace;

import com.google.auto.service.AutoService;
import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties;
import io.opentelemetry.instrumentation.config.bridge.DeclarativeConfigPropertiesBridgeBuilder;
import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider;
import io.opentelemetry.sdk.trace.SpanProcessor;

@SuppressWarnings("rawtypes")
@AutoService(ComponentProvider.class)
public class StackTraceComponentProvider implements ComponentProvider<SpanProcessor> {
@Override
public String getName() {
return "experimental_stacktrace";
}

@Override
public SpanProcessor create(DeclarativeConfigProperties config) {
return StackTraceAutoConfig.create(
new DeclarativeConfigPropertiesBridgeBuilder()
.addMapping(StackTraceAutoConfig.CONFIG_MIN_DURATION, "min_duration")
Copy link
Contributor

@robsunday robsunday Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR looks good to me and the comment below is not directly related to it.
When looking at this real life sample use of mappings I have an impression that it is easy to make a mistake in some cases. This is because order of adding mappings matters. If more strict mapping is added after more general, then it will never be executed, like

addMapping("aa.bb", "v1");
addMapping("aa.bb.cc", "v2");

What do you think about adding validation to DeclarativeConfigProperitesBridgeBuilder class to make sure that more specific mapping is always added earlier?
Instead of validation, we could sort the entries automatically in the builder by using TreeMap instead of LinkedHashMap.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@robsunday can you open an issue for this so we don't lose it?

.addMapping(StackTraceAutoConfig.PREFIX, "")
.build(config));
}

@Override
public Class<SpanProcessor> getType() {
return SpanProcessor.class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,14 @@ private static String removeInternalFrames(String stackTrace) {
}
return stackTrace.substring(nextNewLine + 1);
}

@Override
public String toString() {
return "StackTraceSpanProcessor{"
+ "minSpanDurationNanos="
+ minSpanDurationNanos
+ ", filterPredicate="
+ filterPredicate
+ '}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.stacktrace;

import static org.assertj.core.api.Assertions.assertThat;

import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfiguration;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;

class StackTraceComponentProviderTest {
@Test
void endToEnd() {
String yaml =
"file_format: 1.0-rc.1\n"
+ "tracer_provider:\n"
+ " processors:\n"
+ " - experimental_stacktrace: \n"
+ " min_duration: 100\n"
+ " filter: io.opentelemetry.contrib.stacktrace.StackTraceSpanProcessorTest$YesPredicate\n";

OpenTelemetrySdk openTelemetrySdk =
DeclarativeConfiguration.parseAndCreate(
new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8)));

assertThat(openTelemetrySdk.getSdkTracerProvider().toString())
.contains(
String.format(
Locale.ROOT,
"StackTraceSpanProcessor{minSpanDurationNanos=%d, "
+ "filterPredicate=io.opentelemetry.contrib.stacktrace.StackTraceSpanProcessorTest$YesPredicate",
TimeUnit.MILLISECONDS.toNanos(100)));
}
}
Loading