Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ private static boolean isRunningAfterDatadogAgent(List<AgentKind> agents) {
}

private static void enableOtelSDKAutoconfiguration() {
// Silence agent-internal SLF4J output by default
setPropertyIfAbsent("org.slf4j.simpleLogger.log.dev.braintrust", "warn");

// Enable OTel SDK autoconfiguration. When anyone first calls
// GlobalOpenTelemetry.get(), the SDK will be built using autoconfigure, which
// discovers our BraintrustAutoConfigCustomizer via ServiceLoader and injects the
Expand Down
3 changes: 3 additions & 0 deletions braintrust-java-agent/internal/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ dependencies {
// SLF4J API — needed at compile time for Lombok's @Slf4j annotation
implementation "org.slf4j:slf4j-api:${slf4jVersion}"

// SLF4J Simple binding — bundled so the agent always has a logging backend
runtimeOnly "org.slf4j:slf4j-simple:${slf4jVersion}"

// ByteBuddy for bytecode manipulation — bundled as .classdata in BraintrustClassLoader
implementation 'net.bytebuddy:byte-buddy:1.17.5'

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package dev.braintrust.agent;

import com.google.auto.service.AutoService;
import dev.braintrust.Braintrust;
import dev.braintrust.bootstrap.BraintrustBridge;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer;
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@AutoService(AutoConfigurationCustomizerProvider.class)
public class OtelAutoConfig implements AutoConfigurationCustomizerProvider {
@Override
public void customize(AutoConfigurationCustomizer autoConfiguration) {
autoConfiguration.addTracerProviderCustomizer(
((sdkTracerProviderBuilder, configProperties) -> {
if (!BraintrustBridge.otelInstallCount.compareAndSet(0, 1)) {
log.warn(
"otel install invoked more than once. This should not happen."
+ " Bailing.");
return sdkTracerProviderBuilder;
}
Braintrust.get()
.openTelemetryEnable(
sdkTracerProviderBuilder,
SdkLoggerProvider.builder(),
SdkMeterProvider.builder());
return sdkTracerProviderBuilder;
}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,24 @@ static void replayTrace(Tracer tracer, List<SpanData> spans) {

// Link to parent context if available
String parentSpanId = sd.getParentSpanContext().getSpanId();
// Determine the base context to use for this span (used both for setParent and for
// storing in the map so that grandchildren inherit the full ancestor chain).
Context effectiveParentCtx;
if (sd.getParentSpanContext().isValid()) {
Context parentCtx = spanContextMap.get(parentSpanId);
if (parentCtx != null) {
builder.setParent(parentCtx);
Context localParentCtx = spanContextMap.get(parentSpanId);
if (localParentCtx != null) {
// Parent was replayed in this batch — use its stored context so the full
// ancestor chain is preserved for grandchildren.
effectiveParentCtx = localParentCtx;
builder.setParent(effectiveParentCtx);
} else {
// Parent not in this batch — create a remote parent context
builder.setParent(Context.current().with(Span.wrap(sd.getParentSpanContext())));
// Parent not in this batch — create a remote parent context.
effectiveParentCtx =
Context.current().with(Span.wrap(sd.getParentSpanContext()));
builder.setParent(effectiveParentCtx);
}
} else {
effectiveParentCtx = Context.root();
builder.setNoParent();
}

Expand Down Expand Up @@ -155,8 +164,10 @@ static void replayTrace(Tracer tracer, List<SpanData> spans) {
span.setStatus(StatusCode.OK, sd.getStatus().getDescription());
}

// Store the context for potential children
Context ctx = Context.current().with(span);
// Store the context for potential children, nested within the effective parent context
// so that grandchildren see the full ancestor chain (e.g. for braintrust.parent
// propagation via BraintrustSpanProcessor.onStart).
Context ctx = effectiveParentCtx.with(span);
spanContextMap.put(sd.getSpanContext().getSpanId(), ctx);

// End with original end timestamp
Expand Down
Loading
Loading