Skip to content
Draft
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
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ apiValidation {
"sentry-samples-spring-boot-4",
"sentry-samples-spring-boot-4-opentelemetry",
"sentry-samples-spring-boot-4-opentelemetry-noagent",
"sentry-samples-spring-boot-4-otlp",
"sentry-samples-spring-boot-4-webflux",
"sentry-samples-ktor-client",
"sentry-uitest-android",
Expand Down
1 change: 1 addition & 0 deletions buildSrc/src/main/java/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ object Config {
val SENTRY_SPRING_BOOT_4_STARTER_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.spring-boot-4-starter"
val SENTRY_OPENTELEMETRY_BOOTSTRAP_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.opentelemetry.bootstrap"
val SENTRY_OPENTELEMETRY_CORE_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.opentelemetry.core"
val SENTRY_OPENTELEMETRY_OTLP_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.opentelemetry.otlp"
val SENTRY_OPENTELEMETRY_AGENT_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.opentelemetry.agent"
val SENTRY_OPENTELEMETRY_AGENTLESS_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.opentelemetry.agentless"
val SENTRY_OPENTELEMETRY_AGENTLESS_SPRING_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.opentelemetry.agentless-spring"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
public final class io/sentry/opentelemetry/OpenTelemetryAttributesExtractor {
public fun <init> ()V
public fun extract (Lio/opentelemetry/sdk/trace/data/SpanData;Lio/sentry/IScope;Lio/sentry/SentryOptions;)V
public fun extractUrl (Lio/opentelemetry/api/common/Attributes;Lio/sentry/SentryOptions;)Ljava/lang/String;
}

public final class io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor : io/sentry/EventProcessor {
public fun <init> ()V
public fun getOrder ()Ljava/lang/Long;
public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent;
}

public final class io/sentry/opentelemetry/OtelInternalSpanDetectionUtil {
public fun <init> ()V
public static fun isSentryRequest (Lio/sentry/IScopes;Lio/opentelemetry/api/trace/SpanKind;Lio/opentelemetry/api/common/Attributes;)Z
}

public final class io/sentry/opentelemetry/OtelSamplingUtil {
public fun <init> ()V
public static fun extractSamplingDecision (Lio/opentelemetry/api/common/Attributes;)Lio/sentry/TracesSamplingDecision;
}

public final class io/sentry/opentelemetry/OtelSentryPropagator : io/opentelemetry/context/propagation/TextMapPropagator {
public fun <init> ()V
public fun extract (Lio/opentelemetry/context/Context;Ljava/lang/Object;Lio/opentelemetry/context/propagation/TextMapGetter;)Lio/opentelemetry/context/Context;
public fun fields ()Ljava/util/Collection;
public fun inject (Lio/opentelemetry/context/Context;Ljava/lang/Object;Lio/opentelemetry/context/propagation/TextMapSetter;)V
}

public final class io/sentry/opentelemetry/OtelSentrySpanProcessor : io/opentelemetry/sdk/trace/SpanProcessor {
public fun <init> ()V
public fun isEndRequired ()Z
public fun isStartRequired ()Z
public fun onEnd (Lio/opentelemetry/sdk/trace/ReadableSpan;)V
public fun onStart (Lio/opentelemetry/context/Context;Lio/opentelemetry/sdk/trace/ReadWriteSpan;)V
}

public final class io/sentry/opentelemetry/OtelSpanContext : io/sentry/SpanContext {
public fun <init> (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/sentry/TracesSamplingDecision;Lio/sentry/opentelemetry/IOtelSpanWrapper;Lio/sentry/SpanId;Lio/sentry/Baggage;)V
public fun getOperation ()Ljava/lang/String;
public fun getStatus ()Lio/sentry/SpanStatus;
public fun setOperation (Ljava/lang/String;)V
public fun setStatus (Lio/sentry/SpanStatus;)V
}

public final class io/sentry/opentelemetry/OtelSpanInfo {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V
public fun getDescription ()Ljava/lang/String;
public fun getOp ()Ljava/lang/String;
public fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource;
}

public final class io/sentry/opentelemetry/OtelSpanUtils {
public fun <init> ()V
public static fun maybeTransferOtelAttribute (Lio/opentelemetry/sdk/trace/data/SpanData;Lio/sentry/ISpan;Lio/opentelemetry/api/common/AttributeKey;)V
}

public final class io/sentry/opentelemetry/OtelSpanWrapper : io/sentry/opentelemetry/IOtelSpanWrapper {
public fun <init> (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/sentry/IScopes;Lio/sentry/SentryDate;Lio/sentry/TracesSamplingDecision;Lio/sentry/opentelemetry/IOtelSpanWrapper;Lio/sentry/SpanId;Lio/sentry/Baggage;)V
public fun addFeatureFlag (Ljava/lang/String;Ljava/lang/Boolean;)V
public fun finish ()V
public fun finish (Lio/sentry/SpanStatus;)V
public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V
public fun getContexts ()Lio/sentry/protocol/Contexts;
public fun getData ()Ljava/util/Map;
public fun getData (Ljava/lang/String;)Ljava/lang/Object;
public fun getDescription ()Ljava/lang/String;
public fun getFinishDate ()Lio/sentry/SentryDate;
public fun getMeasurements ()Ljava/util/Map;
public fun getOpenTelemetrySpanAttributes ()Lio/opentelemetry/api/common/Attributes;
public fun getOperation ()Ljava/lang/String;
public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision;
public fun getScopes ()Lio/sentry/IScopes;
public fun getSpanContext ()Lio/sentry/SpanContext;
public fun getStartDate ()Lio/sentry/SentryDate;
public fun getStatus ()Lio/sentry/SpanStatus;
public fun getTag (Ljava/lang/String;)Ljava/lang/String;
public fun getTags ()Ljava/util/Map;
public fun getThrowable ()Ljava/lang/Throwable;
public fun getTraceId ()Lio/sentry/protocol/SentryId;
public fun getTransactionName ()Ljava/lang/String;
public fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource;
public fun isFinished ()Z
public fun isNoOp ()Z
public fun isProfileSampled ()Ljava/lang/Boolean;
public fun isSampled ()Ljava/lang/Boolean;
public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken;
public fun setContext (Ljava/lang/String;Ljava/lang/Object;)V
public fun setData (Ljava/lang/String;Ljava/lang/Object;)V
public fun setDescription (Ljava/lang/String;)V
public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;)V
public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;Lio/sentry/MeasurementUnit;)V
public fun setOperation (Ljava/lang/String;)V
public fun setStatus (Lio/sentry/SpanStatus;)V
public fun setTag (Ljava/lang/String;Ljava/lang/String;)V
public fun setThrowable (Ljava/lang/Throwable;)V
public fun setTransactionName (Ljava/lang/String;)V
public fun setTransactionName (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V
public fun startChild (Lio/sentry/SpanContext;Lio/sentry/SpanOptions;)Lio/sentry/ISpan;
public fun startChild (Ljava/lang/String;)Lio/sentry/ISpan;
public fun startChild (Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ISpan;
public fun startChild (Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryDate;Lio/sentry/Instrumenter;)Lio/sentry/ISpan;
public fun startChild (Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryDate;Lio/sentry/Instrumenter;Lio/sentry/SpanOptions;)Lio/sentry/ISpan;
public fun startChild (Ljava/lang/String;Ljava/lang/String;Lio/sentry/SpanOptions;)Lio/sentry/ISpan;
public fun storeInContext (Lio/opentelemetry/context/Context;)Lio/opentelemetry/context/Context;
public fun toBaggageHeader (Ljava/util/List;)Lio/sentry/BaggageHeader;
public fun toSentryTrace ()Lio/sentry/SentryTraceHeader;
public fun traceContext ()Lio/sentry/TraceContext;
public fun updateEndDate (Lio/sentry/SentryDate;)Z
}

public final class io/sentry/opentelemetry/SentryPropagator : io/opentelemetry/context/propagation/TextMapPropagator {
public fun <init> ()V
public fun extract (Lio/opentelemetry/context/Context;Ljava/lang/Object;Lio/opentelemetry/context/propagation/TextMapGetter;)Lio/opentelemetry/context/Context;
public fun fields ()Ljava/util/Collection;
public fun inject (Lio/opentelemetry/context/Context;Ljava/lang/Object;Lio/opentelemetry/context/propagation/TextMapSetter;)V
}

public final class io/sentry/opentelemetry/SentrySampler : io/opentelemetry/sdk/trace/samplers/Sampler {
public fun <init> ()V
public fun <init> (Lio/sentry/IScopes;)V
public fun getDescription ()Ljava/lang/String;
public fun shouldSample (Lio/opentelemetry/context/Context;Ljava/lang/String;Ljava/lang/String;Lio/opentelemetry/api/trace/SpanKind;Lio/opentelemetry/api/common/Attributes;Ljava/util/List;)Lio/opentelemetry/sdk/trace/samplers/SamplingResult;
}

public final class io/sentry/opentelemetry/SentrySamplingResult : io/opentelemetry/sdk/trace/samplers/SamplingResult {
public fun <init> (Lio/sentry/TracesSamplingDecision;)V
public fun getAttributes ()Lio/opentelemetry/api/common/Attributes;
public fun getDecision ()Lio/opentelemetry/sdk/trace/samplers/SamplingDecision;
public fun getSentryDecision ()Lio/sentry/TracesSamplingDecision;
}

public final class io/sentry/opentelemetry/SentrySpanExporter : io/opentelemetry/sdk/trace/export/SpanExporter {
public static final field TRACE_ORIGIN Ljava/lang/String;
public fun <init> ()V
public fun <init> (Lio/sentry/IScopes;)V
public fun export (Ljava/util/Collection;)Lio/opentelemetry/sdk/common/CompletableResultCode;
public fun flush ()Lio/opentelemetry/sdk/common/CompletableResultCode;
public fun shutdown ()Lio/opentelemetry/sdk/common/CompletableResultCode;
}

public final class io/sentry/opentelemetry/SentrySpanProcessor : io/opentelemetry/sdk/trace/SpanProcessor {
public fun <init> ()V
public fun isEndRequired ()Z
public fun isStartRequired ()Z
public fun onEnd (Lio/opentelemetry/sdk/trace/ReadableSpan;)V
public fun onStart (Lio/opentelemetry/context/Context;Lio/opentelemetry/sdk/trace/ReadWriteSpan;)V
}

public final class io/sentry/opentelemetry/SpanDescriptionExtractor {
public fun <init> ()V
public fun extractSpanInfo (Lio/opentelemetry/sdk/trace/data/SpanData;Lio/sentry/opentelemetry/IOtelSpanWrapper;)Lio/sentry/opentelemetry/OtelSpanInfo;
}

public final class io/sentry/opentelemetry/SpanNode {
public fun <init> (Ljava/lang/String;)V
public fun addChild (Lio/sentry/opentelemetry/SpanNode;)V
public fun addChildren (Ljava/util/List;)V
public fun getChildren ()Ljava/util/List;
public fun getId ()Ljava/lang/String;
public fun getParentNode ()Lio/sentry/opentelemetry/SpanNode;
public fun getSpan ()Lio/opentelemetry/sdk/trace/data/SpanData;
public fun setParentNode (Lio/sentry/opentelemetry/SpanNode;)V
public fun setSpan (Lio/opentelemetry/sdk/trace/data/SpanData;)V
}

public final class io/sentry/opentelemetry/TraceData {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryTraceHeader;Lio/sentry/Baggage;)V
public fun getBaggage ()Lio/sentry/Baggage;
public fun getParentSpanId ()Ljava/lang/String;
public fun getSentryTraceHeader ()Lio/sentry/SentryTraceHeader;
public fun getSpanId ()Ljava/lang/String;
public fun getTraceId ()Ljava/lang/String;
}

81 changes: 81 additions & 0 deletions sentry-opentelemetry/sentry-opentelemetry-otlp/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import net.ltgt.gradle.errorprone.errorprone
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
`java-library`
id("io.sentry.javadoc")
alias(libs.plugins.kotlin.jvm)
jacoco
alias(libs.plugins.errorprone)
alias(libs.plugins.gradle.versions)
}

tasks.withType<KotlinCompile>().configureEach {
compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8
}

dependencies {
api(projects.sentry)

compileOnly(libs.otel)
// compileOnly(libs.otel.semconv)
// compileOnly(libs.otel.semconv.incubating)

compileOnly(libs.jetbrains.annotations)
compileOnly(libs.nopen.annotations)
errorprone(libs.errorprone.core)
errorprone(libs.nopen.checker)
errorprone(libs.nullaway)

// tests
testImplementation(projects.sentryTestSupport)
testImplementation(kotlin(Config.kotlinStdLib))
testImplementation(libs.awaitility.kotlin)
testImplementation(libs.kotlin.test.junit)
testImplementation(libs.mockito.kotlin)

testImplementation(libs.otel)
// testImplementation(libs.otel.semconv)
// testImplementation(libs.otel.semconv.incubating)
}

configure<SourceSetContainer> { test { java.srcDir("src/test/java") } }

jacoco { toolVersion = libs.versions.jacoco.get() }

tasks.jacocoTestReport {
reports {
xml.required.set(true)
html.required.set(false)
}
}

tasks {
jacocoTestCoverageVerification {
violationRules { rule { limit { minimum = Config.QualityPlugins.Jacoco.minimumCoverage } } }
}
check {
dependsOn(jacocoTestCoverageVerification)
dependsOn(jacocoTestReport)
}
}

tasks.withType<JavaCompile>().configureEach {
options.errorprone {
check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR)
option("NullAway:AnnotatedPackages", "io.sentry")
}
}

tasks.jar {
manifest {
attributes(
"Sentry-Version-Name" to project.version,
"Sentry-SDK-Name" to Config.Sentry.SENTRY_OPENTELEMETRY_OTLP_SDK_NAME,
"Sentry-SDK-Package-Name" to "maven:io.sentry:sentry-opentelemetry-otlp",
"Implementation-Vendor" to "Sentry",
"Implementation-Title" to project.name,
"Implementation-Version" to project.version,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package io.sentry.opentelemetry.otlp;

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanId;
import io.opentelemetry.api.trace.TraceId;
import io.sentry.EventProcessor;
import io.sentry.Hint;
import io.sentry.IScopes;
import io.sentry.ScopesAdapter;
import io.sentry.SentryEvent;
import io.sentry.SentryLevel;
import io.sentry.SentryLogEvent;
import io.sentry.SentryMetricsEvent;
import io.sentry.SpanContext;
import io.sentry.protocol.SentryId;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;

public final class OpenTelemetryOtlpEventProcessor implements EventProcessor {

private final @NotNull IScopes scopes;

public OpenTelemetryOtlpEventProcessor() {
this(ScopesAdapter.getInstance());
}

@TestOnly
OpenTelemetryOtlpEventProcessor(final @NotNull IScopes scopes) {
this.scopes = scopes;
}

@Override
public @Nullable SentryEvent process(final @NotNull SentryEvent event, final @NotNull Hint hint) {
@NotNull final Span otelSpan = Span.current();
@NotNull final String traceId = otelSpan.getSpanContext().getTraceId();
@NotNull final String spanId = otelSpan.getSpanContext().getSpanId();

if (TraceId.isValid(traceId) && SpanId.isValid(spanId)) {
final @NotNull SpanContext spanContext =
new SpanContext(
new SentryId(traceId), new io.sentry.SpanId(spanId), "opentelemetry", null, null);

event.getContexts().setTrace(spanContext);
scopes
.getOptions()
.getLogger()
.log(
SentryLevel.DEBUG,
"Linking Sentry event %s to span %s created via OpenTelemetry (trace %s).",
event.getEventId(),
spanId,
traceId);
} else {
scopes
.getOptions()
.getLogger()
.log(
SentryLevel.DEBUG,
"Not linking Sentry event %s to any transaction created via OpenTelemetry as traceId %s or spanId %s are invalid.",
event.getEventId(),
traceId,
spanId);
}

return event;
}

@Override
public @Nullable SentryLogEvent process(@NotNull SentryLogEvent event) {
@NotNull final Span otelSpan = Span.current();
@NotNull final String traceId = otelSpan.getSpanContext().getTraceId();
@NotNull final String spanId = otelSpan.getSpanContext().getSpanId();

if (TraceId.isValid(traceId) && SpanId.isValid(spanId)) {
event.setTraceId(new SentryId(traceId));
event.setSpanId(new io.sentry.SpanId(spanId));
} else {
scopes
.getOptions()
.getLogger()
.log(
SentryLevel.DEBUG,
"Not linking Sentry event to any transaction created via OpenTelemetry as traceId %s or spanId %s are invalid.",
traceId,
spanId);
}

return event;
}

@Override
public @Nullable SentryMetricsEvent process(
@NotNull SentryMetricsEvent event, @NotNull Hint hint) {
@NotNull final Span otelSpan = Span.current();
@NotNull final String traceId = otelSpan.getSpanContext().getTraceId();
@NotNull final String spanId = otelSpan.getSpanContext().getSpanId();

if (TraceId.isValid(traceId) && SpanId.isValid(spanId)) {
event.setTraceId(new SentryId(traceId));
event.setSpanId(new io.sentry.SpanId(spanId));
} else {
scopes
.getOptions()
.getLogger()
.log(
SentryLevel.DEBUG,
"Not linking Sentry event to any transaction created via OpenTelemetry as traceId %s or spanId %s are invalid.",
traceId,
spanId);
}

return event;
}

@Override
public @Nullable Long getOrder() {
return 6000L;
}
}
Loading