-
Notifications
You must be signed in to change notification settings - Fork 130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Inferred spans #1340
Merged
+7,538
−1
Merged
Inferred spans #1340
Changes from 12 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
044d9ba
Copied inferred-spans project over from elastic distro
JonasKunz a59677d
Renamed to contrib package
JonasKunz e2539ee
Fix compilation errors, make tests run
JonasKunz 41597d6
Fix warnings
JonasKunz 43a87b6
Replaced copied collections with explicit agrona dependency
JonasKunz ce4369d
fixed warnings
JonasKunz b2a3c41
Removed NullAway supressions
JonasKunz 2177fa0
Added test for incubating attribute
JonasKunz 0e38de3
Added test for incubating attributes
JonasKunz 2a1442c
Moved implementations to internal package
JonasKunz 3c0d8c7
Reworked readme
JonasKunz 9593f1d
Use project version as tracer version
JonasKunz ec490be
Fix dead link in README
JonasKunz 80d86f7
markdown fixes
JonasKunz 8ccf1fe
More markdown and misspell fixes
JonasKunz ebebbd8
Regenerate recording.jfr with new package name, add instructions how …
JonasKunz 82a3959
Removed additional references to elastic/apm
JonasKunz e59e13d
Merge remote-tracking branch 'otel/main' into inferred-spans
JonasKunz cbcdb5a
Fix invalid clock usage on certain CPU models
JonasKunz edd8572
spotless fix
JonasKunz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -70,3 +70,7 @@ components: | |
- jackshirazi | ||
- jonaskunz | ||
- sylvainjuge | ||
inferred-spans: | ||
- jackshirazi | ||
- jonaskunz | ||
- sylvainjuge |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import net.ltgt.gradle.errorprone.errorprone | ||
|
||
plugins { | ||
id("otel.java-conventions") | ||
id("otel.publish-conventions") | ||
} | ||
|
||
description = "OpenTelemetry Java profiling based inferred spans module" | ||
otelJava.moduleName.set("io.opentelemetry.contrib.inferredspans") | ||
|
||
dependencies { | ||
annotationProcessor("com.google.auto.service:auto-service") | ||
compileOnly("com.google.auto.service:auto-service-annotations") | ||
compileOnly("io.opentelemetry:opentelemetry-sdk") | ||
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") | ||
implementation("com.lmax:disruptor") | ||
implementation("org.jctools:jctools-core") | ||
implementation("tools.profiler:async-profiler") | ||
implementation("com.blogspot.mydailyjava:weak-lock-free") | ||
implementation("org.agrona:agrona") | ||
|
||
testAnnotationProcessor("com.google.auto.service:auto-service") | ||
testCompileOnly("com.google.auto.service:auto-service-annotations") | ||
testImplementation("io.opentelemetry.semconv:opentelemetry-semconv-incubating:1.25.0-alpha") | ||
testImplementation("io.opentelemetry:opentelemetry-sdk") | ||
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") | ||
testImplementation("io.opentelemetry:opentelemetry-sdk-testing") | ||
testImplementation("io.opentelemetry:opentelemetry-api-incubator") | ||
testImplementation("io.opentelemetry:opentelemetry-exporter-logging") | ||
} | ||
|
||
tasks { | ||
withType<JavaCompile>().configureEach { | ||
with(options) { | ||
errorprone { | ||
// This code uses nullable reference in many places due to performance | ||
// and makes assumptions of when these references are non-null | ||
// In the code we express those assumptions as assertions | ||
// instead of Object.requireNonNull because the NPEs raised by actual | ||
// null dereferencing are more helpful than the ones raised by Object.requireNonNull | ||
option("NullAway:AssertsEnabled", "true") | ||
} | ||
} | ||
} | ||
} |
113 changes: 113 additions & 0 deletions
113
...d-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansAutoConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.contrib.inferredspans; | ||
|
||
import com.google.auto.service.AutoService; | ||
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; | ||
import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; | ||
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; | ||
import java.time.Duration; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.function.Consumer; | ||
import java.util.logging.Logger; | ||
import java.util.stream.Collectors; | ||
import javax.annotation.Nullable; | ||
|
||
@AutoService(AutoConfigurationCustomizerProvider.class) | ||
public class InferredSpansAutoConfig implements AutoConfigurationCustomizerProvider { | ||
|
||
private static final Logger log = Logger.getLogger(InferredSpansAutoConfig.class.getName()); | ||
|
||
static final String ENABLED_OPTION = "otel.inferred.spans.enabled"; | ||
static final String LOGGING_OPTION = "otel.inferred.spans.logging.enabled"; | ||
static final String DIAGNOSTIC_FILES_OPTION = "otel.inferred.spans.backup.diagnostic.files"; | ||
static final String SAFEMODE_OPTION = "otel.inferred.spans.safe.mode"; | ||
static final String POSTPROCESSING_OPTION = "otel.inferred.spans.post.processing.enabled"; | ||
static final String SAMPLING_INTERVAL_OPTION = "otel.inferred.spans.sampling.interval"; | ||
static final String MIN_DURATION_OPTION = "otel.inferred.spans.min.duration"; | ||
static final String INCLUDED_CLASSES_OPTION = "otel.inferred.spans.included.classes"; | ||
static final String EXCLUDED_CLASSES_OPTION = "otel.inferred.spans.excluded.classes"; | ||
static final String INTERVAL_OPTION = "otel.inferred.spans.interval"; | ||
static final String DURATION_OPTION = "otel.inferred.spans.duration"; | ||
static final String LIB_DIRECTORY_OPTION = "otel.inferred.spans.lib.directory"; | ||
|
||
@Override | ||
public void customize(AutoConfigurationCustomizer config) { | ||
config.addTracerProviderCustomizer( | ||
(providerBuilder, properties) -> { | ||
if (properties.getBoolean(ENABLED_OPTION, false)) { | ||
InferredSpansProcessorBuilder builder = InferredSpansProcessor.builder(); | ||
|
||
PropertiesApplier applier = new PropertiesApplier(properties); | ||
|
||
applier.applyBool(LOGGING_OPTION, builder::profilerLoggingEnabled); | ||
applier.applyBool(DIAGNOSTIC_FILES_OPTION, builder::backupDiagnosticFiles); | ||
applier.applyInt(SAFEMODE_OPTION, builder::asyncProfilerSafeMode); | ||
applier.applyBool(POSTPROCESSING_OPTION, builder::postProcessingEnabled); | ||
applier.applyDuration(SAMPLING_INTERVAL_OPTION, builder::samplingInterval); | ||
applier.applyDuration(MIN_DURATION_OPTION, builder::inferredSpansMinDuration); | ||
applier.applyWildcards(INCLUDED_CLASSES_OPTION, builder::includedClasses); | ||
applier.applyWildcards(EXCLUDED_CLASSES_OPTION, builder::excludedClasses); | ||
applier.applyDuration(INTERVAL_OPTION, builder::profilerInterval); | ||
applier.applyDuration(DURATION_OPTION, builder::profilingDuration); | ||
applier.applyString(LIB_DIRECTORY_OPTION, builder::profilerLibDirectory); | ||
|
||
providerBuilder.addSpanProcessor(builder.build()); | ||
} else { | ||
log.finest( | ||
"Not enabling inferred spans processor because " + ENABLED_OPTION + " is not set"); | ||
} | ||
return providerBuilder; | ||
}); | ||
} | ||
|
||
private static class PropertiesApplier { | ||
|
||
private final ConfigProperties properties; | ||
|
||
public PropertiesApplier(ConfigProperties properties) { | ||
this.properties = properties; | ||
} | ||
|
||
public void applyBool(String configKey, Consumer<Boolean> funcToApply) { | ||
applyValue(properties.getBoolean(configKey), funcToApply); | ||
} | ||
|
||
public void applyInt(String configKey, Consumer<Integer> funcToApply) { | ||
applyValue(properties.getInt(configKey), funcToApply); | ||
} | ||
|
||
public void applyDuration(String configKey, Consumer<Duration> funcToApply) { | ||
applyValue(properties.getDuration(configKey), funcToApply); | ||
} | ||
|
||
public void applyString(String configKey, Consumer<String> funcToApply) { | ||
applyValue(properties.getString(configKey), funcToApply); | ||
} | ||
|
||
public void applyWildcards( | ||
String configKey, Consumer<? super List<WildcardMatcher>> funcToApply) { | ||
String wildcardListString = properties.getString(configKey); | ||
if (wildcardListString != null && !wildcardListString.isEmpty()) { | ||
List<WildcardMatcher> values = | ||
Arrays.stream(wildcardListString.split(",")) | ||
.filter(str -> !str.isEmpty()) | ||
.map(WildcardMatcher::valueOf) | ||
.collect(Collectors.toList()); | ||
if (!values.isEmpty()) { | ||
funcToApply.accept(values); | ||
} | ||
} | ||
} | ||
|
||
private static <T> void applyValue(@Nullable T value, Consumer<T> funcToApply) { | ||
if (value != null) { | ||
funcToApply.accept(value); | ||
} | ||
} | ||
} | ||
} |
143 changes: 143 additions & 0 deletions
143
...ed-spans/src/main/java/io/opentelemetry/contrib/inferredspans/InferredSpansProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.contrib.inferredspans; | ||
|
||
import io.opentelemetry.api.GlobalOpenTelemetry; | ||
import io.opentelemetry.api.trace.Tracer; | ||
import io.opentelemetry.api.trace.TracerProvider; | ||
import io.opentelemetry.context.Context; | ||
import io.opentelemetry.contrib.inferredspans.internal.InferredSpansConfiguration; | ||
import io.opentelemetry.contrib.inferredspans.internal.SamplingProfiler; | ||
import io.opentelemetry.contrib.inferredspans.internal.SpanAnchoredClock; | ||
import io.opentelemetry.sdk.common.CompletableResultCode; | ||
import io.opentelemetry.sdk.trace.ReadWriteSpan; | ||
import io.opentelemetry.sdk.trace.ReadableSpan; | ||
import io.opentelemetry.sdk.trace.SpanProcessor; | ||
import java.io.File; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.util.Objects; | ||
import java.util.Properties; | ||
import java.util.concurrent.Executors; | ||
import java.util.concurrent.ThreadFactory; | ||
import java.util.function.Supplier; | ||
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
import javax.annotation.Nullable; | ||
|
||
public class InferredSpansProcessor implements SpanProcessor { | ||
|
||
private static final Logger logger = Logger.getLogger(InferredSpansProcessor.class.getName()); | ||
|
||
public static final String TRACER_NAME = "inferred-spans"; | ||
|
||
public static final String TRACER_VERSION = readInferredSpansVersion(); | ||
|
||
// Visible for testing | ||
final SamplingProfiler profiler; | ||
|
||
private Supplier<TracerProvider> tracerProvider = GlobalOpenTelemetry::getTracerProvider; | ||
|
||
@Nullable private volatile Tracer tracer; | ||
|
||
InferredSpansProcessor( | ||
InferredSpansConfiguration config, | ||
SpanAnchoredClock clock, | ||
boolean startScheduledProfiling, | ||
@Nullable File activationEventsFile, | ||
@Nullable File jfrFile) { | ||
profiler = new SamplingProfiler(config, clock, this::getTracer, activationEventsFile, jfrFile); | ||
if (startScheduledProfiling) { | ||
profiler.start(); | ||
} | ||
} | ||
|
||
public static InferredSpansProcessorBuilder builder() { | ||
return new InferredSpansProcessorBuilder(); | ||
} | ||
|
||
/** | ||
* Allows customization of the TraceProvider to use. If not set, a TraceProvider from {@link | ||
* GlobalOpenTelemetry} will be used. | ||
* | ||
* @param provider the provider to use. Null means that {@link GlobalOpenTelemetry} will be used | ||
* lazily. | ||
*/ | ||
public synchronized void setTracerProvider(TracerProvider provider) { | ||
if (provider == null) { | ||
this.tracerProvider = GlobalOpenTelemetry::getTracerProvider; | ||
} else { | ||
this.tracerProvider = () -> provider; | ||
} | ||
} | ||
|
||
@Override | ||
public void onStart(Context parentContext, ReadWriteSpan span) { | ||
profiler.getClock().onSpanStart(span, parentContext); | ||
} | ||
|
||
@Override | ||
public boolean isStartRequired() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public void onEnd(ReadableSpan span) {} | ||
|
||
@Override | ||
public boolean isEndRequired() { | ||
return false; | ||
} | ||
|
||
@Override | ||
@SuppressWarnings({"FutureReturnValueIgnored", "InterruptedExceptionSwallowed"}) | ||
public CompletableResultCode shutdown() { | ||
CompletableResultCode result = new CompletableResultCode(); | ||
logger.fine("Stopping Inferred Spans Processor"); | ||
ThreadFactory threadFactory = | ||
r -> { | ||
Thread thread = new Thread(r); | ||
thread.setDaemon(false); | ||
thread.setName("otel-inferred-spans-shutdown"); | ||
return thread; | ||
}; | ||
Executors.newSingleThreadExecutor(threadFactory) | ||
.submit( | ||
() -> { | ||
try { | ||
profiler.stop(); | ||
result.succeed(); | ||
} catch (Throwable e) { | ||
logger.log(Level.SEVERE, "Failed to stop Inferred Spans Processor", e); | ||
result.fail(); | ||
} | ||
}); | ||
return result; | ||
} | ||
|
||
private Tracer getTracer() { | ||
if (tracer == null) { | ||
synchronized (this) { | ||
if (tracer == null) { | ||
tracer = tracerProvider.get().get(TRACER_NAME, TRACER_VERSION); | ||
} | ||
} | ||
} | ||
return tracer; | ||
} | ||
|
||
private static String readInferredSpansVersion() { | ||
try (InputStream is = InferredSpansProcessor.class.getResourceAsStream("version.properties")) { | ||
Properties properties = new Properties(); | ||
properties.load(is); | ||
String version = (String) properties.get("contrib.version"); | ||
Objects.requireNonNull(version); | ||
return version; | ||
} catch (IOException e) { | ||
throw new IllegalStateException(e); | ||
} | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any recommended namespace here to use for the config options? Or is
otel.inferred.spans
fine?