diff --git a/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/JvmMetricsInstaller.java b/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/JvmMetricsInstaller.java index fed727687..c0e0980ec 100644 --- a/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/JvmMetricsInstaller.java +++ b/instrumentation/jvm-metrics/src/main/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/JvmMetricsInstaller.java @@ -22,8 +22,6 @@ import com.splunk.opentelemetry.instrumentation.jvmmetrics.otel.OtelAllocatedMemoryMetrics; import com.splunk.opentelemetry.instrumentation.jvmmetrics.otel.OtelGcMemoryMetrics; import com.splunk.opentelemetry.profiler.ProfilerConfiguration; -import com.splunk.opentelemetry.profiler.ProfilerDeclarativeConfiguration; -import com.splunk.opentelemetry.profiler.ProfilerEnvVarsConfiguration; import io.opentelemetry.javaagent.extension.AgentListener; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; @@ -33,7 +31,7 @@ public class JvmMetricsInstaller implements AgentListener { @Override public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) { ConfigProperties config = getConfig(autoConfiguredOpenTelemetrySdk); - boolean metricsEnabled = isProfilerMemoryEnabled(config); + boolean metricsEnabled = isProfilerMemoryEnabled(); if (!config.getBoolean("otel.instrumentation.jvm-metrics-splunk.enabled", metricsEnabled)) { return; @@ -43,12 +41,7 @@ public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetr new OtelGcMemoryMetrics().install(); } - private boolean isProfilerMemoryEnabled(ConfigProperties config) { - ProfilerConfiguration profilerConfiguration = - ProfilerDeclarativeConfiguration.SUPPLIER.isConfigured() - ? ProfilerDeclarativeConfiguration.SUPPLIER.get() - : new ProfilerEnvVarsConfiguration(config); - - return profilerConfiguration.getMemoryEnabled(); + private boolean isProfilerMemoryEnabled() { + return ProfilerConfiguration.SUPPLIER.get().getMemoryEnabled(); } } diff --git a/instrumentation/jvm-metrics/src/test/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/JvmMetricsInstallerTest.java b/instrumentation/jvm-metrics/src/test/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/JvmMetricsInstallerTest.java index 7aa2c2e95..81c48d29d 100644 --- a/instrumentation/jvm-metrics/src/test/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/JvmMetricsInstallerTest.java +++ b/instrumentation/jvm-metrics/src/test/java/com/splunk/opentelemetry/instrumentation/jvmmetrics/JvmMetricsInstallerTest.java @@ -23,6 +23,8 @@ import static org.mockito.Mockito.verify; import com.splunk.opentelemetry.instrumentation.jvmmetrics.otel.OtelAllocatedMemoryMetrics; +import com.splunk.opentelemetry.profiler.ProfilerConfiguration; +import com.splunk.opentelemetry.profiler.ProfilerEnvVarsConfigurationFactory; import com.splunk.opentelemetry.testing.declarativeconfig.DeclarativeConfigTestUtil; import io.opentelemetry.instrumentation.config.bridge.DeclarativeConfigPropertiesBridgeBuilder; import io.opentelemetry.sdk.autoconfigure.AutoConfigureUtil; @@ -30,12 +32,18 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import java.util.Map; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.mockito.MockedConstruction; import org.mockito.MockedStatic; class JvmMetricsInstallerTest { + @AfterEach + void tearDown() { + ProfilerConfiguration.SUPPLIER.reset(); + } + @Test void shouldInstallJvmMetrics_declarativeConfig() { // given @@ -53,6 +61,7 @@ void shouldInstallJvmMetrics_declarativeConfig() { new DeclarativeConfigPropertiesBridgeBuilder() .build( AutoConfigureUtil.getInstrumentationConfig(DeclarativeConfigTestUtil.parse(yaml))); + ProfilerConfiguration.SUPPLIER.configure(ProfilerEnvVarsConfigurationFactory.create(config)); try (MockedStatic autoConfigureUtil = mockStatic(AutoConfigureUtil.class); MockedConstruction allocatedMetrics = @@ -78,6 +87,7 @@ void shouldInstallJvmMetrics_envVarsConfig() { ConfigProperties config = DefaultConfigProperties.createFromMap( Map.of("otel.instrumentation.jvm-metrics-splunk.enabled", "true")); + ProfilerConfiguration.SUPPLIER.configure(ProfilerEnvVarsConfigurationFactory.create(config)); try (MockedStatic autoConfigureUtil = mockStatic(AutoConfigureUtil.class); MockedConstruction allocatedMetrics = diff --git a/licenses/commons-jexl3-3.6.2.jar/META-INF/LICENSE.txt b/licenses/commons-jexl3-3.6.3.jar/META-INF/LICENSE.txt similarity index 100% rename from licenses/commons-jexl3-3.6.2.jar/META-INF/LICENSE.txt rename to licenses/commons-jexl3-3.6.3.jar/META-INF/LICENSE.txt diff --git a/licenses/commons-jexl3-3.6.2.jar/META-INF/NOTICE.txt b/licenses/commons-jexl3-3.6.3.jar/META-INF/NOTICE.txt similarity index 100% rename from licenses/commons-jexl3-3.6.2.jar/META-INF/NOTICE.txt rename to licenses/commons-jexl3-3.6.3.jar/META-INF/NOTICE.txt diff --git a/licenses/licenses.md b/licenses/licenses.md index 91bf2d05f..a79680819 100644 --- a/licenses/licenses.md +++ b/licenses/licenses.md @@ -1,7 +1,7 @@ # splunk-otel-javaagent ## Dependency License Report -_2026-06-20 10:34:26 EEST_ +_2026-06-24 14:18:04 CEST_ ## Apache License, Version 2.0 **1** **Group:** `com.squareup.okhttp3` **Name:** `okhttp` **Version:** `5.4.0` @@ -92,12 +92,12 @@ _2026-06-20 10:34:26 EEST_ > - **POM Project URL**: [https://github.com/open-telemetry/opentelemetry-java-instrumentation](https://github.com/open-telemetry/opentelemetry-java-instrumentation) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -**23** **Group:** `org.apache.commons` **Name:** `commons-jexl3` **Version:** `3.6.2` +**23** **Group:** `org.apache.commons` **Name:** `commons-jexl3` **Version:** `3.6.3` > - **Project URL**: [https://commons.apache.org/proper/commons-jexl/](https://commons.apache.org/proper/commons-jexl/) > - **Manifest License**: [https://www.apache.org/licenses/LICENSE-2.0](Apache License, Version 2.0) > - **POM License**: Apache License, Version 2.0 - [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) -> - **Embedded license files**: [commons-jexl3-3.6.2.jar/META-INF/LICENSE.txt](commons-jexl3-3.6.2.jar/META-INF/LICENSE.txt) - - [commons-jexl3-3.6.2.jar/META-INF/NOTICE.txt](commons-jexl3-3.6.2.jar/META-INF/NOTICE.txt) +> - **Embedded license files**: [commons-jexl3-3.6.3.jar/META-INF/LICENSE.txt](commons-jexl3-3.6.3.jar/META-INF/LICENSE.txt) + - [commons-jexl3-3.6.3.jar/META-INF/NOTICE.txt](commons-jexl3-3.6.3.jar/META-INF/NOTICE.txt) **24** **Group:** `org.jetbrains` **Name:** `annotations` **Version:** `13.0` > - **POM Project URL**: [http://www.jetbrains.org](http://www.jetbrains.org) diff --git a/opamp/src/main/java/com/splunk/opentelemetry/opamp/DeclarativeEffectiveConfigFileFactory.java b/opamp/src/main/java/com/splunk/opentelemetry/opamp/DeclarativeEffectiveConfigFileFactory.java index a3410f895..7ab53d8af 100644 --- a/opamp/src/main/java/com/splunk/opentelemetry/opamp/DeclarativeEffectiveConfigFileFactory.java +++ b/opamp/src/main/java/com/splunk/opentelemetry/opamp/DeclarativeEffectiveConfigFileFactory.java @@ -18,7 +18,6 @@ import com.google.common.annotations.VisibleForTesting; import com.splunk.opentelemetry.profiler.ProfilerConfiguration; -import com.splunk.opentelemetry.profiler.ProfilerDeclarativeConfiguration; import com.splunk.opentelemetry.profiler.snapshot.SnapshotProfilingConfiguration; import com.splunk.opentelemetry.profiler.snapshot.SnapshotProfilingDeclarativeConfiguration; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.LogRecordExporterModel; @@ -69,7 +68,7 @@ public String createEffectiveConfigContent() { if (model == null) { return ""; } - ProfilerConfiguration profilerConfiguration = ProfilerDeclarativeConfiguration.SUPPLIER.get(); + ProfilerConfiguration profilerConfiguration = ProfilerConfiguration.SUPPLIER.get(); SnapshotProfilingConfiguration snapshotConfiguration = SnapshotProfilingDeclarativeConfiguration.SUPPLIER.get(); diff --git a/opamp/src/main/java/com/splunk/opentelemetry/opamp/EnvVarsEffectiveConfigFileFactory.java b/opamp/src/main/java/com/splunk/opentelemetry/opamp/EnvVarsEffectiveConfigFileFactory.java index 62186f822..021a5ae1a 100644 --- a/opamp/src/main/java/com/splunk/opentelemetry/opamp/EnvVarsEffectiveConfigFileFactory.java +++ b/opamp/src/main/java/com/splunk/opentelemetry/opamp/EnvVarsEffectiveConfigFileFactory.java @@ -17,7 +17,7 @@ package com.splunk.opentelemetry.opamp; import com.splunk.opentelemetry.profiler.ProfilerConfiguration; -import com.splunk.opentelemetry.profiler.ProfilerEnvVarsConfiguration; +import com.splunk.opentelemetry.profiler.ProfilerEnvVarsConfigurationFactory; import com.splunk.opentelemetry.profiler.snapshot.SnapshotProfilingConfiguration; import com.splunk.opentelemetry.profiler.snapshot.SnapshotProfilingEnvVarsConfiguration; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; @@ -49,7 +49,8 @@ public String createEffectiveConfigContent() { } private void addSplunkEnvVars(EffectiveConfigBuilder builder) { - ProfilerConfiguration profilerConfiguration = new ProfilerEnvVarsConfiguration(config); + ProfilerConfiguration profilerConfiguration = + ProfilerEnvVarsConfigurationFactory.create(config); SnapshotProfilingConfiguration snapshotConfiguration = new SnapshotProfilingEnvVarsConfiguration(config); diff --git a/opamp/src/main/java/com/splunk/opentelemetry/opamp/RemoteConfigProcessor.java b/opamp/src/main/java/com/splunk/opentelemetry/opamp/RemoteConfigProcessor.java index d25adfff0..92881a41e 100644 --- a/opamp/src/main/java/com/splunk/opentelemetry/opamp/RemoteConfigProcessor.java +++ b/opamp/src/main/java/com/splunk/opentelemetry/opamp/RemoteConfigProcessor.java @@ -19,7 +19,8 @@ import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; import com.google.common.annotations.VisibleForTesting; -import com.splunk.opentelemetry.profiler.ProfilerDeclarativeConfiguration; +import com.splunk.opentelemetry.profiler.ProfilerConfiguration; +import com.splunk.opentelemetry.profiler.ProfilerDeclarativeConfigurationFactory; import com.splunk.opentelemetry.profiler.ProfilingSupervisor; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; import io.opentelemetry.opamp.client.OpampClient; @@ -69,8 +70,8 @@ public void applyConfig(AgentRemoteConfig remoteConfig, OpampClient opampClient) if (splunkDistributionConfigProperties.getPropertyKeys().contains(PROFILING_NODE_NAME)) { DeclarativeConfigProperties profilingConfigProperties = splunkDistributionConfigProperties.getStructured(PROFILING_NODE_NAME, empty()); - ProfilerDeclarativeConfiguration profilingConfig = - new ProfilerDeclarativeConfiguration(profilingConfigProperties); + ProfilerConfiguration profilingConfig = + ProfilerDeclarativeConfigurationFactory.create(profilingConfigProperties); // TODO: should be merged with current profiling config. Probably we will need profiler // configuration refactoring and some listeners implemented for profiler configuration // changes. For POC use this temporary solution diff --git a/opamp/src/test/java/com/splunk/opentelemetry/opamp/DeclarativeEffectiveConfigFileFactoryTest.java b/opamp/src/test/java/com/splunk/opentelemetry/opamp/DeclarativeEffectiveConfigFileFactoryTest.java index 0bbf444ff..6e0c44e88 100644 --- a/opamp/src/test/java/com/splunk/opentelemetry/opamp/DeclarativeEffectiveConfigFileFactoryTest.java +++ b/opamp/src/test/java/com/splunk/opentelemetry/opamp/DeclarativeEffectiveConfigFileFactoryTest.java @@ -23,7 +23,7 @@ import static org.mockito.Mockito.when; import com.splunk.opentelemetry.profiler.ProfilerConfiguration; -import com.splunk.opentelemetry.profiler.ProfilerDeclarativeConfiguration; +import com.splunk.opentelemetry.profiler.ProfilerDeclarativeConfigurationFactory; import com.splunk.opentelemetry.profiler.snapshot.SnapshotProfilingConfiguration; import com.splunk.opentelemetry.profiler.snapshot.SnapshotProfilingDeclarativeConfiguration; import com.splunk.opentelemetry.testing.declarativeconfig.DeclarativeConfigTestUtil; @@ -56,7 +56,7 @@ class DeclarativeEffectiveConfigFileFactoryTest { @AfterEach void afterEach() { DeclarativeConfigurationInterceptor.reset(); - ProfilerDeclarativeConfiguration.SUPPLIER.reset(); + ProfilerConfiguration.SUPPLIER.reset(); SnapshotProfilingDeclarativeConfiguration.SUPPLIER.reset(); } @@ -396,8 +396,8 @@ public static void main(String[] args) throws Exception { DeclarativeConfigProperties profilingConfig = getDistributionConfig(model).getStructured("profiling", empty()); - ProfilerDeclarativeConfiguration.SUPPLIER.configure( - new ProfilerDeclarativeConfiguration(profilingConfig)); + ProfilerConfiguration.SUPPLIER.configure( + ProfilerDeclarativeConfigurationFactory.create(profilingConfig)); SnapshotProfilingDeclarativeConfiguration.SUPPLIER.configure( new SnapshotProfilingDeclarativeConfiguration(profilingConfig)); diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrAgentListener.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrAgentListener.java index fc978ef53..cf8ea81e7 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrAgentListener.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrAgentListener.java @@ -19,9 +19,7 @@ import com.google.auto.service.AutoService; import com.google.common.annotations.VisibleForTesting; import io.opentelemetry.javaagent.extension.AgentListener; -import io.opentelemetry.sdk.autoconfigure.AutoConfigureUtil; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.util.logging.Logger; @AutoService(AgentListener.class) @@ -40,12 +38,14 @@ public JfrAgentListener() { @Override public void afterAgent(AutoConfiguredOpenTelemetrySdk sdk) { - ProfilingSupervisor.setupJfrContextStorage(); + if (jfr.isAvailable()) { + ProfilingSupervisor.setupJfrContextStorage(); + } - ProfilerConfiguration config = getProfilerConfiguration(sdk); // Always start the supervisor, so it can start profiling later elsewhere. - ProfilingSupervisor supervisor = makeProfilingSupervisor(sdk, config); + ProfilingSupervisor supervisor = makeProfilingSupervisor(sdk); + ProfilerConfiguration config = ProfilerConfiguration.SUPPLIER.get(); if (notClearForTakeoff(config)) { return; } @@ -60,19 +60,8 @@ public int order() { } // Exists for testing - ProfilingSupervisor makeProfilingSupervisor( - AutoConfiguredOpenTelemetrySdk sdk, ProfilerConfiguration config) { - return ProfilingSupervisor.createAndStart(sdk, config); - } - - private static ProfilerConfiguration getProfilerConfiguration( - AutoConfiguredOpenTelemetrySdk sdk) { - if (ProfilerDeclarativeConfiguration.SUPPLIER.isConfigured()) { - return ProfilerDeclarativeConfiguration.SUPPLIER.get(); - } else { - ConfigProperties configProperties = AutoConfigureUtil.getConfig(sdk); - return new ProfilerEnvVarsConfiguration(configProperties); - } + ProfilingSupervisor makeProfilingSupervisor(AutoConfiguredOpenTelemetrySdk sdk) { + return ProfilingSupervisor.createAndStart(sdk); } private boolean notClearForTakeoff(ProfilerConfiguration config) { diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/LogExporterBuilder.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/LogExporterBuilder.java index ea7c781c6..ec4e5f364 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/LogExporterBuilder.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/LogExporterBuilder.java @@ -44,7 +44,8 @@ class LogExporterBuilder { static final String EXTRA_CONTENT_TYPE = "Extra-Content-Type"; static final String STACKTRACES_HEADER_VALUE = "otel-profiling-stacktraces"; - static LogRecordExporter fromConfig(DeclarativeConfigProperties exporterConfigProperties) { + static LogRecordExporter fromDeclarativeConfig( + DeclarativeConfigProperties exporterConfigProperties) { if (exporterConfigProperties != null) { Set propertyKeys = exporterConfigProperties.getPropertyKeys(); @@ -68,11 +69,8 @@ static LogRecordExporter fromConfig(DeclarativeConfigProperties exporterConfigPr throw new ConfigurationException("Profiler exporter configuration is invalid"); } - static LogRecordExporter fromConfig(ConfigProperties config) { - return fromConfig(new ProfilerEnvVarsConfiguration(config)); - } - - static LogRecordExporter fromConfig(ProfilerEnvVarsConfiguration config) { + static LogRecordExporter fromEnvironmentConfig() { + ProfilerConfiguration config = ProfilerConfiguration.SUPPLIER.get(); String protocol = config.getOtlpProtocol(); if ("http/protobuf".equals(protocol)) { return buildHttpExporter(config, OtlpHttpLogRecordExporter::builder); @@ -84,7 +82,7 @@ static LogRecordExporter fromConfig(ProfilerEnvVarsConfiguration config) { @VisibleForTesting static LogRecordExporter buildGrpcExporter( - ProfilerEnvVarsConfiguration config, Supplier makeBuilder) { + ProfilerConfiguration config, Supplier makeBuilder) { OtlpGrpcLogRecordExporterBuilder builder = makeBuilder.get(); String ingestUrl = config.getIngestUrl(); builder.setEndpoint(ingestUrl); @@ -94,13 +92,13 @@ static LogRecordExporter buildGrpcExporter( @VisibleForTesting static LogRecordExporter buildHttpExporter( - ProfilerEnvVarsConfiguration config, Supplier makeBuilder) { + ProfilerConfiguration config, Supplier makeBuilder) { OtlpHttpLogRecordExporterBuilder builder = makeBuilder.get(); String ingestUrl = config.getIngestUrl(); OtlpConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_LOGS, - config.getConfigProperties(), + (ConfigProperties) config.getConfigProperties(), builder::setComponentLoader, builder::setEndpoint, builder::addHeader, diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/OtelLoggerFactory.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/OtelLoggerFactory.java index f164fc902..2bddc274a 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/OtelLoggerFactory.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/OtelLoggerFactory.java @@ -26,26 +26,27 @@ import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor; import io.opentelemetry.sdk.resources.Resource; import java.util.function.Function; +import java.util.function.Supplier; public class OtelLoggerFactory { - private final Function logRecordExporter; + private final Supplier logRecordExporter; private final Function declarativeLogRecordExporter; public OtelLoggerFactory() { - this(LogExporterBuilder::fromConfig, LogExporterBuilder::fromConfig); + this(LogExporterBuilder::fromEnvironmentConfig, LogExporterBuilder::fromDeclarativeConfig); } @VisibleForTesting public OtelLoggerFactory( - Function logRecordExporter, + Supplier logRecordExporter, Function declarativeLogRecordExporter) { this.logRecordExporter = logRecordExporter; this.declarativeLogRecordExporter = declarativeLogRecordExporter; } public Logger build(ConfigProperties properties, Resource resource) { - LogRecordExporter exporter = createLogRecordExporter(properties); + LogRecordExporter exporter = createLogRecordExporter(); LogRecordProcessor processor = SimpleLogRecordProcessor.create(exporter); return buildOtelLogger(processor, resource); } @@ -56,8 +57,8 @@ public Logger build(DeclarativeConfigProperties properties, Resource resource) { return buildOtelLogger(processor, resource); } - private LogRecordExporter createLogRecordExporter(ConfigProperties properties) { - return logRecordExporter.apply(properties); + private LogRecordExporter createLogRecordExporter() { + return logRecordExporter.get(); } private LogRecordExporter createLogRecordExporter(DeclarativeConfigProperties properties) { diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/PeriodicRecordingFlusherBuilder.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/PeriodicRecordingFlusherBuilder.java index 00efd8a62..54297452a 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/PeriodicRecordingFlusherBuilder.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/PeriodicRecordingFlusherBuilder.java @@ -25,7 +25,6 @@ import com.splunk.opentelemetry.profiler.exporter.CpuEventExporter; import com.splunk.opentelemetry.profiler.exporter.PprofCpuEventExporter; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.logs.LogRecordProcessor; import io.opentelemetry.sdk.logs.SdkLoggerProvider; import io.opentelemetry.sdk.logs.export.LogRecordExporter; @@ -162,13 +161,9 @@ private static LogRecordExporter createLogRecordExporter(Object configProperties if (configProperties instanceof DeclarativeConfigProperties) { DeclarativeConfigProperties exporterConfig = ((DeclarativeConfigProperties) configProperties).getStructured("exporter", empty()); - return LogExporterBuilder.fromConfig(exporterConfig); + return LogExporterBuilder.fromDeclarativeConfig(exporterConfig); } - if (configProperties instanceof ConfigProperties) { - return LogExporterBuilder.fromConfig((ConfigProperties) configProperties); - } - throw new IllegalArgumentException( - "Unsupported config properties type: " + configProperties.getClass().getName()); + return LogExporterBuilder.fromEnvironmentConfig(); } private boolean checkOutputDir(Path outputDir) { diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfiguration.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfiguration.java index 6e2aaf69f..12593b2a2 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfiguration.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfiguration.java @@ -16,50 +16,289 @@ package com.splunk.opentelemetry.profiler; +import com.splunk.opentelemetry.profiler.util.OptionalConfigurableSupplier; import java.time.Duration; +import java.util.Objects; +import java.util.logging.Logger; +import javax.annotation.Nullable; -public interface ProfilerConfiguration { - boolean HAS_OBJECT_ALLOCATION_SAMPLE_EVENT = getJavaVersion() >= 16; +public class ProfilerConfiguration { + public static final OptionalConfigurableSupplier SUPPLIER = + new OptionalConfigurableSupplier<>(); - boolean isEnabled(); + public static final boolean HAS_OBJECT_ALLOCATION_SAMPLE_EVENT = getJavaVersion() >= 16; - void log(); + private static final Logger logger = Logger.getLogger(ProfilerConfiguration.class.getName()); + private static final String DEFAULT_PROFILER_DIRECTORY = System.getProperty("java.io.tmpdir"); + private static final Duration DEFAULT_RECORDING_DURATION = Duration.ofSeconds(20); + private static final Duration DEFAULT_CALL_STACK_INTERVAL = Duration.ofSeconds(10); - String getIngestUrl(); + private final boolean enabled; + @Nullable private final String ingestUrl; + @Nullable private final String otlpProtocol; + private final boolean memoryEnabled; + private final boolean memoryEventRateLimitEnabled; + private final String memoryEventRate; + private final boolean useAllocationSampleEvent; + private final Duration callStackInterval; + private final boolean includeAgentInternalStacks; + private final boolean includeJvmInternalStacks; + private final boolean tracingStacksOnly; + private final int stackDepth; + private final boolean keepFiles; + private final String profilerDirectory; + private final Duration recordingDuration; + @Nullable private final Object configProperties; - String getOtlpProtocol(); + private ProfilerConfiguration(Builder builder) { + enabled = builder.enabled; + ingestUrl = builder.ingestUrl; + otlpProtocol = builder.otlpProtocol; + memoryEnabled = builder.memoryEnabled; + memoryEventRateLimitEnabled = builder.memoryEventRateLimitEnabled; + memoryEventRate = builder.memoryEventRate; + useAllocationSampleEvent = builder.useAllocationSampleEvent; + callStackInterval = builder.callStackInterval; + includeAgentInternalStacks = builder.includeAgentInternalStacks; + includeJvmInternalStacks = builder.includeJvmInternalStacks; + tracingStacksOnly = builder.tracingStacksOnly; + stackDepth = builder.stackDepth; + keepFiles = builder.keepFiles; + profilerDirectory = builder.profilerDirectory; + recordingDuration = builder.recordingDuration; + configProperties = builder.configProperties; + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return new Builder(this); + } - boolean getMemoryEnabled(); + public boolean isEnabled() { + return enabled; + } + + public void log() { + logger.info("-----------------------"); + logger.info("Profiler configuration:"); + log("Enabled", isEnabled()); + log("ProfilerDirectory", getProfilerDirectory()); + log("RecordingDuration", getRecordingDuration().toMillis() + "ms"); + log("KeepFiles", getKeepFiles()); + log("OtlpProtocol", getOtlpProtocol()); + log("IngestUrl", getIngestUrl()); + log("MemoryEnabled", getMemoryEnabled()); + if (getMemoryEventRateLimitEnabled()) { + log("MemoryEventRate", getMemoryEventRate()); + } + log("UseAllocationSampleEvent", getUseAllocationSampleEvent()); + log("CallStackInterval", getCallStackInterval().toMillis() + "ms"); + log("IncludeAgentInternalStacks", getIncludeAgentInternalStacks()); + log("IncludeJvmInternalStacks", getIncludeJvmInternalStacks()); + log("TracingStacksOnly", getTracingStacksOnly()); + log("StackDepth", getStackDepth()); + logger.info("-----------------------"); + } + + private static void log(String key, @Nullable Object value) { + logger.info(String.format("%30s : %s", key, value)); + } + + @Nullable + public String getIngestUrl() { + return ingestUrl; + } + + @Nullable + public String getOtlpProtocol() { + return otlpProtocol; + } + + public boolean getMemoryEnabled() { + return memoryEnabled; + } - boolean getMemoryEventRateLimitEnabled(); + public boolean getMemoryEventRateLimitEnabled() { + return memoryEventRateLimitEnabled; + } - String getMemoryEventRate(); + public String getMemoryEventRate() { + return memoryEventRate; + } - boolean getUseAllocationSampleEvent(); + public boolean getUseAllocationSampleEvent() { + return useAllocationSampleEvent; + } - Duration getCallStackInterval(); + public Duration getCallStackInterval() { + return callStackInterval; + } - boolean getIncludeAgentInternalStacks(); + public boolean getIncludeAgentInternalStacks() { + return includeAgentInternalStacks; + } - boolean getIncludeJvmInternalStacks(); + public boolean getIncludeJvmInternalStacks() { + return includeJvmInternalStacks; + } - boolean getTracingStacksOnly(); + public boolean getTracingStacksOnly() { + return tracingStacksOnly; + } - int getStackDepth(); + public int getStackDepth() { + return stackDepth; + } - boolean getKeepFiles(); + public boolean getKeepFiles() { + return keepFiles; + } - String getProfilerDirectory(); + public String getProfilerDirectory() { + return profilerDirectory; + } - Duration getRecordingDuration(); + public Duration getRecordingDuration() { + return recordingDuration; + } - Object getConfigProperties(); + @Nullable + public Object getConfigProperties() { + return configProperties; + } - static int getJavaVersion() { + public static int getJavaVersion() { String javaSpecVersion = System.getProperty("java.specification.version"); if ("1.8".equals(javaSpecVersion)) { return 8; } return Integer.parseInt(javaSpecVersion); } + + public static class Builder { + private boolean enabled; + @Nullable private String ingestUrl; + @Nullable private String otlpProtocol; + private boolean memoryEnabled; + private boolean memoryEventRateLimitEnabled = true; + private String memoryEventRate = "150/s"; + private boolean useAllocationSampleEvent; + private Duration callStackInterval = DEFAULT_CALL_STACK_INTERVAL; + private boolean includeAgentInternalStacks; + private boolean includeJvmInternalStacks; + private boolean tracingStacksOnly; + private int stackDepth = 1024; + private boolean keepFiles; + private String profilerDirectory = DEFAULT_PROFILER_DIRECTORY; + private Duration recordingDuration = DEFAULT_RECORDING_DURATION; + @Nullable private Object configProperties; + + private Builder() {} + + private Builder(ProfilerConfiguration config) { + enabled = config.enabled; + ingestUrl = config.ingestUrl; + otlpProtocol = config.otlpProtocol; + memoryEnabled = config.memoryEnabled; + memoryEventRateLimitEnabled = config.memoryEventRateLimitEnabled; + memoryEventRate = config.memoryEventRate; + useAllocationSampleEvent = config.useAllocationSampleEvent; + callStackInterval = config.callStackInterval; + includeAgentInternalStacks = config.includeAgentInternalStacks; + includeJvmInternalStacks = config.includeJvmInternalStacks; + tracingStacksOnly = config.tracingStacksOnly; + stackDepth = config.stackDepth; + keepFiles = config.keepFiles; + profilerDirectory = config.profilerDirectory; + recordingDuration = config.recordingDuration; + configProperties = config.configProperties; + } + + public ProfilerConfiguration build() { + return new ProfilerConfiguration(this); + } + + public Builder setEnabled(boolean enabled) { + this.enabled = enabled; + return this; + } + + public Builder setIngestUrl(@Nullable String ingestUrl) { + this.ingestUrl = ingestUrl; + return this; + } + + public Builder setOtlpProtocol(@Nullable String otlpProtocol) { + this.otlpProtocol = otlpProtocol; + return this; + } + + public Builder setMemoryEnabled(boolean memoryEnabled) { + this.memoryEnabled = memoryEnabled; + return this; + } + + public Builder setMemoryEventRateLimitEnabled(boolean memoryEventRateLimitEnabled) { + this.memoryEventRateLimitEnabled = memoryEventRateLimitEnabled; + return this; + } + + public Builder setMemoryEventRate(String memoryEventRate) { + this.memoryEventRate = Objects.requireNonNull(memoryEventRate); + return this; + } + + public Builder setUseAllocationSampleEvent(boolean useAllocationSampleEvent) { + this.useAllocationSampleEvent = useAllocationSampleEvent; + return this; + } + + public Builder setCallStackInterval(Duration callStackInterval) { + this.callStackInterval = Objects.requireNonNull(callStackInterval); + return this; + } + + public Builder setIncludeAgentInternalStacks(boolean includeAgentInternalStacks) { + this.includeAgentInternalStacks = includeAgentInternalStacks; + return this; + } + + public Builder setIncludeJvmInternalStacks(boolean includeJvmInternalStacks) { + this.includeJvmInternalStacks = includeJvmInternalStacks; + return this; + } + + public Builder setTracingStacksOnly(boolean tracingStacksOnly) { + this.tracingStacksOnly = tracingStacksOnly; + return this; + } + + public Builder setStackDepth(int stackDepth) { + this.stackDepth = stackDepth; + return this; + } + + public Builder setKeepFiles(boolean keepFiles) { + this.keepFiles = keepFiles; + return this; + } + + public Builder setProfilerDirectory(String profilerDirectory) { + this.profilerDirectory = Objects.requireNonNull(profilerDirectory); + return this; + } + + public Builder setRecordingDuration(Duration recordingDuration) { + this.recordingDuration = Objects.requireNonNull(recordingDuration); + return this; + } + + public Builder setConfigProperties(@Nullable Object configProperties) { + this.configProperties = configProperties; + return this; + } + } } diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerDeclarativeConfigurationInitializer.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationInitializer.java similarity index 56% rename from profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerDeclarativeConfigurationInitializer.java rename to profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationInitializer.java index 72fba6659..830655de7 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerDeclarativeConfigurationInitializer.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationInitializer.java @@ -23,16 +23,24 @@ import io.opentelemetry.sdk.autoconfigure.AutoConfigureUtil; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizer; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.DeclarativeConfigurationCustomizerProvider; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; +import java.util.Collections; /** - * Purpose of this class is to configure the supplier of ProfilerDeclarativeConfiguration. - * ProfilerDeclarativeConfiguration class object can then be used in code executed after SDK is - * created, such as AgentListeners. + * Purpose of this class is to configure the supplier of ProfilerConfiguration. The configured + * object can then be used in code executed after SDK is created, such as AgentListeners. */ -@AutoService(DeclarativeConfigurationCustomizerProvider.class) -public class ProfilerDeclarativeConfigurationInitializer - implements DeclarativeConfigurationCustomizerProvider { +@AutoService({ + DeclarativeConfigurationCustomizerProvider.class, + AutoConfigurationCustomizerProvider.class +}) +public class ProfilerConfigurationInitializer + implements DeclarativeConfigurationCustomizerProvider, AutoConfigurationCustomizerProvider { + + @Override public void customize(DeclarativeConfigurationCustomizer configurationCustomizer) { + // Initialize profiler configuration from declarative config configurationCustomizer.addModelCustomizer( (model) -> { DeclarativeConfigProperties distributionConfig = @@ -40,10 +48,26 @@ public void customize(DeclarativeConfigurationCustomizer configurationCustomizer DeclarativeConfigProperties profilingConfig = distributionConfig.getStructured("profiling", empty()); - ProfilerDeclarativeConfiguration.SUPPLIER.configure( - new ProfilerDeclarativeConfiguration(profilingConfig)); + ProfilerConfiguration.SUPPLIER.configure( + ProfilerDeclarativeConfigurationFactory.create(profilingConfig)); return model; }); } + + @Override + public void customize(AutoConfigurationCustomizer autoConfiguration) { + // Initialize profiler configuration from environment config + autoConfiguration.addPropertiesCustomizer( + configProperties -> { + ProfilerConfiguration.SUPPLIER.configure( + ProfilerEnvVarsConfigurationFactory.create(configProperties)); + return Collections.emptyMap(); + }); + } + + @Override + public int order() { + return Integer.MAX_VALUE; + } } diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerDeclarativeConfiguration.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerDeclarativeConfiguration.java deleted file mode 100644 index 1da14893f..000000000 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerDeclarativeConfiguration.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.splunk.opentelemetry.profiler; - -import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; - -import com.splunk.opentelemetry.profiler.util.OptionalConfigurableSupplier; -import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; -import java.time.Duration; -import java.util.logging.Logger; -import javax.annotation.Nullable; - -public class ProfilerDeclarativeConfiguration implements ProfilerConfiguration { - public static final OptionalConfigurableSupplier SUPPLIER = - new OptionalConfigurableSupplier<>(); - - private static final Logger logger = - Logger.getLogger(ProfilerDeclarativeConfiguration.class.getName()); - - private static final String ROOT_NODE_NAME = "always_on"; - - private static final String DEFAULT_PROFILER_DIRECTORY = System.getProperty("java.io.tmpdir"); - private static final long DEFAULT_RECORDING_DURATION = Duration.ofSeconds(20).toMillis(); - private static final long DEFAULT_SAMPLING_INTERVAL = Duration.ofSeconds(10).toMillis(); - - private static final String MEMORY_PROFILER = "memory_profiler"; - private static final String MEMORY_EVENT_RATE = "event_rate"; - - private final DeclarativeConfigProperties profilingConfig; - - public ProfilerDeclarativeConfiguration(DeclarativeConfigProperties profilingConfig) { - this.profilingConfig = profilingConfig; - } - - @Override - public boolean isEnabled() { - return (profilingConfig != null) && profilingConfig.getPropertyKeys().contains(ROOT_NODE_NAME); - } - - @Override - public void log() { - logger.info("-----------------------"); - logger.info("Profiler declarative configuration:"); - log("Enabled", isEnabled()); - log("ProfilerDirectory", getProfilerDirectory()); - log("RecordingDuration", getRecordingDuration().toMillis() + "ms"); - log("KeepFiles", getKeepFiles()); - log("MemoryEnabled", getMemoryEnabled()); - if (getMemoryEventRateLimitEnabled()) { - log("MemoryEventRate", getMemoryEventRate()); - } - log("UseAllocationSampleEvent", getUseAllocationSampleEvent()); - log("CallStackInterval", getCallStackInterval().toMillis() + "ms"); - log("IncludeAgentInternalStacks", getIncludeAgentInternalStacks()); - log("IncludeJvmInternalStacks", getIncludeJvmInternalStacks()); - log("TracingStacksOnly", getTracingStacksOnly()); - log("StackDepth", getStackDepth()); - logger.info("-----------------------"); - } - - private static void log(String key, @Nullable Object value) { - logger.info(String.format("%30s : %s", key, value)); - } - - @Override - public String getIngestUrl() { - throw new UnsupportedOperationException("Not supported for declarative configuration"); - } - - @Override - public String getOtlpProtocol() { - throw new UnsupportedOperationException("Not supported for declarative configuration"); - } - - @Override - public boolean getMemoryEnabled() { - return getConfigRoot().getPropertyKeys().contains(MEMORY_PROFILER); - } - - @Override - public boolean getMemoryEventRateLimitEnabled() { - return getMemoryProfilerConfig().getString(MEMORY_EVENT_RATE) != null; - } - - @Override - public String getMemoryEventRate() { - return getMemoryProfilerConfig().getString(MEMORY_EVENT_RATE, "150/s"); - } - - @Override - public boolean getUseAllocationSampleEvent() { - // Using jdk16+ ObjectAllocationSample event is disabled by default - return HAS_OBJECT_ALLOCATION_SAMPLE_EVENT - && getMemoryProfilerConfig().getBoolean("native_sampling", false); - } - - @Override - public Duration getCallStackInterval() { - return getDuration( - getConfigRoot().getStructured("cpu_profiler", empty()), - "sampling_interval", - DEFAULT_SAMPLING_INTERVAL); - } - - @Override - public boolean getIncludeAgentInternalStacks() { - return getConfigRoot().getBoolean("include_agent_internals", false); - } - - @Override - public boolean getIncludeJvmInternalStacks() { - return getConfigRoot().getBoolean("include_jvm_internals", false); - } - - @Override - public boolean getTracingStacksOnly() { - return getConfigRoot().getBoolean("tracing_stacks_only", false); - } - - @Override - public int getStackDepth() { - return getConfigRoot().getInt("stack_depth", 1024); - } - - @Override - public boolean getKeepFiles() { - return getConfigRoot().getBoolean("keep_recording_files", false); - } - - @Override - public String getProfilerDirectory() { - return getConfigRoot().getString("recording_directory", DEFAULT_PROFILER_DIRECTORY); - } - - @Override - public Duration getRecordingDuration() { - return getDuration(getConfigRoot(), "recording_duration", DEFAULT_RECORDING_DURATION); - } - - @Override - public DeclarativeConfigProperties getConfigProperties() { - return profilingConfig; - } - - private DeclarativeConfigProperties getConfigRoot() { - return profilingConfig.getStructured(ROOT_NODE_NAME, empty()); - } - - private DeclarativeConfigProperties getMemoryProfilerConfig() { - return getConfigRoot().getStructured(MEMORY_PROFILER, empty()); - } - - private static Duration getDuration( - DeclarativeConfigProperties config, String key, long defaultValue) { - return Duration.ofMillis(config.getLong(key, defaultValue)); - } -} diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerDeclarativeConfigurationFactory.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerDeclarativeConfigurationFactory.java new file mode 100644 index 000000000..ee03d6664 --- /dev/null +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerDeclarativeConfigurationFactory.java @@ -0,0 +1,83 @@ +/* + * Copyright Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.splunk.opentelemetry.profiler; + +import static io.opentelemetry.api.incubator.config.DeclarativeConfigProperties.empty; + +import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; +import java.time.Duration; + +public final class ProfilerDeclarativeConfigurationFactory { + private static final String ROOT_NODE_NAME = "always_on"; + + private static final String DEFAULT_PROFILER_DIRECTORY = System.getProperty("java.io.tmpdir"); + private static final long DEFAULT_RECORDING_DURATION = Duration.ofSeconds(20).toMillis(); + private static final long DEFAULT_SAMPLING_INTERVAL = Duration.ofSeconds(10).toMillis(); + + private static final String MEMORY_PROFILER = "memory_profiler"; + private static final String MEMORY_EVENT_RATE = "event_rate"; + + private ProfilerDeclarativeConfigurationFactory() {} + + public static ProfilerConfiguration create(DeclarativeConfigProperties profilingConfig) { + DeclarativeConfigProperties config = profilingConfig == null ? empty() : profilingConfig; + DeclarativeConfigProperties configRoot = getConfigRoot(config); + DeclarativeConfigProperties memoryProfilerConfig = getMemoryProfilerConfig(configRoot); + + boolean useAllocationSampleEvent = + ProfilerConfiguration.HAS_OBJECT_ALLOCATION_SAMPLE_EVENT + && memoryProfilerConfig.getBoolean("native_sampling", false); + Duration callStackInterval = + getDuration( + configRoot.getStructured("cpu_profiler", empty()), + "sampling_interval", + DEFAULT_SAMPLING_INTERVAL); + + return ProfilerConfiguration.builder() + .setEnabled(config.getPropertyKeys().contains(ROOT_NODE_NAME)) + .setMemoryEnabled(configRoot.getPropertyKeys().contains(MEMORY_PROFILER)) + .setMemoryEventRateLimitEnabled(memoryProfilerConfig.getString(MEMORY_EVENT_RATE) != null) + .setMemoryEventRate(memoryProfilerConfig.getString(MEMORY_EVENT_RATE, "150/s")) + .setUseAllocationSampleEvent(useAllocationSampleEvent) + .setCallStackInterval(callStackInterval) + .setIncludeAgentInternalStacks(configRoot.getBoolean("include_agent_internals", false)) + .setIncludeJvmInternalStacks(configRoot.getBoolean("include_jvm_internals", false)) + .setTracingStacksOnly(configRoot.getBoolean("tracing_stacks_only", false)) + .setStackDepth(configRoot.getInt("stack_depth", 1024)) + .setKeepFiles(configRoot.getBoolean("keep_recording_files", false)) + .setProfilerDirectory( + configRoot.getString("recording_directory", DEFAULT_PROFILER_DIRECTORY)) + .setRecordingDuration( + getDuration(configRoot, "recording_duration", DEFAULT_RECORDING_DURATION)) + .setConfigProperties(config) + .build(); + } + + private static DeclarativeConfigProperties getConfigRoot(DeclarativeConfigProperties config) { + return config.getStructured(ROOT_NODE_NAME, empty()); + } + + private static DeclarativeConfigProperties getMemoryProfilerConfig( + DeclarativeConfigProperties configRoot) { + return configRoot.getStructured(MEMORY_PROFILER, empty()); + } + + private static Duration getDuration( + DeclarativeConfigProperties config, String key, long defaultValue) { + return Duration.ofMillis(config.getLong(key, defaultValue)); + } +} diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerEnvVarsConfiguration.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerEnvVarsConfiguration.java deleted file mode 100644 index 2f11adb70..000000000 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerEnvVarsConfiguration.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.splunk.opentelemetry.profiler; - -import static java.util.logging.Level.WARNING; - -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import java.time.Duration; -import java.util.logging.Logger; -import javax.annotation.Nullable; - -public class ProfilerEnvVarsConfiguration implements ProfilerConfiguration { - private static final Logger logger = - Logger.getLogger(ProfilerEnvVarsConfiguration.class.getName()); - - static final String CONFIG_KEY_OTLP_PROTOCOL = "otel.exporter.otlp.protocol"; - static final String CONFIG_KEY_OTEL_OTLP_URL = "otel.exporter.otlp.endpoint"; - - /* Keys visible for testing */ - public static final String CONFIG_KEY_PROFILER_ENABLED = "splunk.profiler.enabled"; - public static final String CONFIG_KEY_PROFILER_DIRECTORY = "splunk.profiler.directory"; - public static final String CONFIG_KEY_RECORDING_DURATION = "splunk.profiler.recording.duration"; - public static final String CONFIG_KEY_KEEP_FILES = "splunk.profiler.keep-files"; - public static final String CONFIG_KEY_INGEST_URL = "splunk.profiler.logs-endpoint"; - public static final String CONFIG_KEY_PROFILER_OTLP_PROTOCOL = "splunk.profiler.otlp.protocol"; - public static final String CONFIG_KEY_MEMORY_ENABLED = "splunk.profiler.memory.enabled"; - public static final String CONFIG_KEY_MEMORY_EVENT_RATE_LIMIT_ENABLED = - "splunk.profiler.memory.event.rate-limit.enabled"; - public static final String CONFIG_KEY_MEMORY_EVENT_RATE = "splunk.profiler.memory.event.rate"; - public static final String CONFIG_KEY_MEMORY_NATIVE_SAMPLING = - "splunk.profiler.memory.native.sampling"; - public static final String CONFIG_KEY_CALL_STACK_INTERVAL = "splunk.profiler.call.stack.interval"; - public static final String CONFIG_KEY_INCLUDE_AGENT_INTERNALS = - "splunk.profiler.include.agent.internals"; - // Include stacks where every frame starts with jvm/sun/jdk - public static final String CONFIG_KEY_INCLUDE_JVM_INTERNALS = - "splunk.profiler.include.jvm.internals"; - public static final String CONFIG_KEY_INCLUDE_INTERNAL_STACKS = - "splunk.profiler.include.internal.stacks"; - public static final String CONFIG_KEY_TRACING_STACKS_ONLY = "splunk.profiler.tracing.stacks.only"; - public static final String CONFIG_KEY_STACK_DEPTH = "splunk.profiler.max.stack.depth"; - - private static final String DEFAULT_PROFILER_DIRECTORY = System.getProperty("java.io.tmpdir"); - private static final Duration DEFAULT_RECORDING_DURATION = Duration.ofSeconds(20); - private static final Duration DEFAULT_CALL_STACK_INTERVAL = Duration.ofSeconds(10); - - private final ConfigProperties config; - - public ProfilerEnvVarsConfiguration(ConfigProperties config) { - this.config = config; - } - - @Override - public void log() { - logger.info("-----------------------"); - logger.info("Profiler env vars based configuration:"); - log(CONFIG_KEY_PROFILER_ENABLED, isEnabled()); - log(CONFIG_KEY_PROFILER_DIRECTORY, getProfilerDirectory()); - log(CONFIG_KEY_RECORDING_DURATION, getRecordingDuration().toMillis() + "ms"); - log(CONFIG_KEY_KEEP_FILES, getKeepFiles()); - log(CONFIG_KEY_PROFILER_OTLP_PROTOCOL, getOtlpProtocol()); - log(CONFIG_KEY_INGEST_URL, getIngestUrl()); - log(CONFIG_KEY_OTEL_OTLP_URL, config.getString(CONFIG_KEY_OTEL_OTLP_URL)); - log(CONFIG_KEY_MEMORY_ENABLED, getMemoryEnabled()); - if (getMemoryEventRateLimitEnabled()) { - log(CONFIG_KEY_MEMORY_EVENT_RATE, getMemoryEventRate()); - } - log(CONFIG_KEY_MEMORY_NATIVE_SAMPLING, getUseAllocationSampleEvent()); - log(CONFIG_KEY_CALL_STACK_INTERVAL, getCallStackInterval().toMillis() + "ms"); - log(CONFIG_KEY_INCLUDE_AGENT_INTERNALS, getIncludeAgentInternalStacks()); - log(CONFIG_KEY_INCLUDE_JVM_INTERNALS, getIncludeJvmInternalStacks()); - log(CONFIG_KEY_TRACING_STACKS_ONLY, getTracingStacksOnly()); - log(CONFIG_KEY_STACK_DEPTH, getStackDepth()); - logger.info("-----------------------"); - } - - private void log(String key, @Nullable Object value) { - logger.info(String.format("%39s : %s", key, value)); - } - - @Override - public boolean isEnabled() { - return config.getBoolean(CONFIG_KEY_PROFILER_ENABLED, false); - } - - @Override - public String getIngestUrl() { - String ingestUrl = config.getString(CONFIG_KEY_INGEST_URL); - if (ingestUrl == null) { - String defaultIngestUrl = getDefaultLogsEndpoint(); - ingestUrl = config.getString(CONFIG_KEY_OTEL_OTLP_URL, defaultIngestUrl); - - if (ingestUrl.startsWith("https://ingest.") - && ingestUrl.endsWith(".observability.splunkcloud.com")) { - logger.log( - WARNING, - "Profiling data can not be sent to {0}, using {1} instead. " - + "You can override it by setting " - + CONFIG_KEY_INGEST_URL, - new Object[] {ingestUrl, defaultIngestUrl}); - return defaultIngestUrl; - } - - if ("http/protobuf".equals(getOtlpProtocol())) { - ingestUrl = maybeAppendHttpPath(ingestUrl); - } - } - return ingestUrl; - } - - private String maybeAppendHttpPath(String ingestUrl) { - if (!ingestUrl.endsWith("v1/logs")) { - if (!ingestUrl.endsWith("/")) { - ingestUrl += "/"; - } - ingestUrl += "v1/logs"; - } - return ingestUrl; - } - - private String getDefaultLogsEndpoint() { - return "http/protobuf".equals(getOtlpProtocol()) - ? "http://localhost:4318/v1/logs" - : "http://localhost:4317"; - } - - @Override - public String getOtlpProtocol() { - return config.getString( - CONFIG_KEY_PROFILER_OTLP_PROTOCOL, - config.getString(CONFIG_KEY_OTLP_PROTOCOL, "http/protobuf")); - } - - @Override - public boolean getMemoryEnabled() { - return config.getBoolean(CONFIG_KEY_MEMORY_ENABLED, false); - } - - @Override - public boolean getMemoryEventRateLimitEnabled() { - return config.getBoolean(CONFIG_KEY_MEMORY_EVENT_RATE_LIMIT_ENABLED, true); - } - - @Override - public String getMemoryEventRate() { - return config.getString(CONFIG_KEY_MEMORY_EVENT_RATE, "150/s"); - } - - @Override - public boolean getUseAllocationSampleEvent() { - return HAS_OBJECT_ALLOCATION_SAMPLE_EVENT - && config.getBoolean(CONFIG_KEY_MEMORY_NATIVE_SAMPLING, false); - } - - @Override - public Duration getCallStackInterval() { - return config.getDuration(CONFIG_KEY_CALL_STACK_INTERVAL, DEFAULT_CALL_STACK_INTERVAL); - } - - @Override - public boolean getIncludeAgentInternalStacks() { - boolean includeInternals = config.getBoolean(CONFIG_KEY_INCLUDE_INTERNAL_STACKS, false); - return config.getBoolean(CONFIG_KEY_INCLUDE_AGENT_INTERNALS, includeInternals); - } - - @Override - public boolean getIncludeJvmInternalStacks() { - boolean includeInternals = config.getBoolean(CONFIG_KEY_INCLUDE_INTERNAL_STACKS, false); - return config.getBoolean(CONFIG_KEY_INCLUDE_JVM_INTERNALS, includeInternals); - } - - @Override - public boolean getTracingStacksOnly() { - return config.getBoolean(CONFIG_KEY_TRACING_STACKS_ONLY, false); - } - - @Override - public int getStackDepth() { - return config.getInt(CONFIG_KEY_STACK_DEPTH, 1024); - } - - @Override - public boolean getKeepFiles() { - return config.getBoolean(CONFIG_KEY_KEEP_FILES, false); - } - - @Override - public String getProfilerDirectory() { - return config.getString(CONFIG_KEY_PROFILER_DIRECTORY, DEFAULT_PROFILER_DIRECTORY); - } - - @Override - public Duration getRecordingDuration() { - return config.getDuration(CONFIG_KEY_RECORDING_DURATION, DEFAULT_RECORDING_DURATION); - } - - @Override - public ConfigProperties getConfigProperties() { - return config; - } -} diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerEnvVarsConfigurationFactory.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerEnvVarsConfigurationFactory.java new file mode 100644 index 000000000..f4392e751 --- /dev/null +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerEnvVarsConfigurationFactory.java @@ -0,0 +1,138 @@ +/* + * Copyright Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.splunk.opentelemetry.profiler; + +import static java.util.logging.Level.WARNING; + +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import java.time.Duration; +import java.util.logging.Logger; + +public final class ProfilerEnvVarsConfigurationFactory { + private static final Logger logger = + Logger.getLogger(ProfilerEnvVarsConfigurationFactory.class.getName()); + + static final String CONFIG_KEY_OTLP_PROTOCOL = "otel.exporter.otlp.protocol"; + static final String CONFIG_KEY_OTEL_OTLP_URL = "otel.exporter.otlp.endpoint"; + + /* Keys visible for testing */ + static final String CONFIG_KEY_PROFILER_ENABLED = "splunk.profiler.enabled"; + static final String CONFIG_KEY_PROFILER_DIRECTORY = "splunk.profiler.directory"; + static final String CONFIG_KEY_RECORDING_DURATION = "splunk.profiler.recording.duration"; + static final String CONFIG_KEY_KEEP_FILES = "splunk.profiler.keep-files"; + static final String CONFIG_KEY_INGEST_URL = "splunk.profiler.logs-endpoint"; + static final String CONFIG_KEY_PROFILER_OTLP_PROTOCOL = "splunk.profiler.otlp.protocol"; + static final String CONFIG_KEY_MEMORY_ENABLED = "splunk.profiler.memory.enabled"; + static final String CONFIG_KEY_MEMORY_EVENT_RATE_LIMIT_ENABLED = + "splunk.profiler.memory.event.rate-limit.enabled"; + static final String CONFIG_KEY_MEMORY_EVENT_RATE = "splunk.profiler.memory.event.rate"; + static final String CONFIG_KEY_MEMORY_NATIVE_SAMPLING = "splunk.profiler.memory.native.sampling"; + static final String CONFIG_KEY_CALL_STACK_INTERVAL = "splunk.profiler.call.stack.interval"; + static final String CONFIG_KEY_INCLUDE_AGENT_INTERNALS = + "splunk.profiler.include.agent.internals"; + // Include stacks where every frame starts with jvm/sun/jdk + static final String CONFIG_KEY_INCLUDE_JVM_INTERNALS = "splunk.profiler.include.jvm.internals"; + static final String CONFIG_KEY_INCLUDE_INTERNAL_STACKS = + "splunk.profiler.include.internal.stacks"; + static final String CONFIG_KEY_TRACING_STACKS_ONLY = "splunk.profiler.tracing.stacks.only"; + static final String CONFIG_KEY_STACK_DEPTH = "splunk.profiler.max.stack.depth"; + + private static final String DEFAULT_PROFILER_DIRECTORY = System.getProperty("java.io.tmpdir"); + private static final Duration DEFAULT_RECORDING_DURATION = Duration.ofSeconds(20); + private static final Duration DEFAULT_CALL_STACK_INTERVAL = Duration.ofSeconds(10); + + private ProfilerEnvVarsConfigurationFactory() {} + + public static ProfilerConfiguration create(ConfigProperties config) { + String otlpProtocol = getOtlpProtocol(config); + boolean includeInternals = config.getBoolean(CONFIG_KEY_INCLUDE_INTERNAL_STACKS, false); + + boolean useAllocationSampleEvent = + ProfilerConfiguration.HAS_OBJECT_ALLOCATION_SAMPLE_EVENT + && config.getBoolean(CONFIG_KEY_MEMORY_NATIVE_SAMPLING, false); + return ProfilerConfiguration.builder() + .setEnabled(config.getBoolean(CONFIG_KEY_PROFILER_ENABLED, false)) + .setIngestUrl(getIngestUrl(config, otlpProtocol)) + .setOtlpProtocol(otlpProtocol) + .setMemoryEnabled(config.getBoolean(CONFIG_KEY_MEMORY_ENABLED, false)) + .setMemoryEventRateLimitEnabled( + config.getBoolean(CONFIG_KEY_MEMORY_EVENT_RATE_LIMIT_ENABLED, true)) + .setMemoryEventRate(config.getString(CONFIG_KEY_MEMORY_EVENT_RATE, "150/s")) + .setUseAllocationSampleEvent(useAllocationSampleEvent) + .setCallStackInterval( + config.getDuration(CONFIG_KEY_CALL_STACK_INTERVAL, DEFAULT_CALL_STACK_INTERVAL)) + .setIncludeAgentInternalStacks( + config.getBoolean(CONFIG_KEY_INCLUDE_AGENT_INTERNALS, includeInternals)) + .setIncludeJvmInternalStacks( + config.getBoolean(CONFIG_KEY_INCLUDE_JVM_INTERNALS, includeInternals)) + .setTracingStacksOnly(config.getBoolean(CONFIG_KEY_TRACING_STACKS_ONLY, false)) + .setStackDepth(config.getInt(CONFIG_KEY_STACK_DEPTH, 1024)) + .setKeepFiles(config.getBoolean(CONFIG_KEY_KEEP_FILES, false)) + .setProfilerDirectory( + config.getString(CONFIG_KEY_PROFILER_DIRECTORY, DEFAULT_PROFILER_DIRECTORY)) + .setRecordingDuration( + config.getDuration(CONFIG_KEY_RECORDING_DURATION, DEFAULT_RECORDING_DURATION)) + .setConfigProperties(config) + .build(); + } + + private static String getIngestUrl(ConfigProperties config, String otlpProtocol) { + String ingestUrl = config.getString(CONFIG_KEY_INGEST_URL); + if (ingestUrl == null) { + String defaultIngestUrl = getDefaultLogsEndpoint(otlpProtocol); + ingestUrl = config.getString(CONFIG_KEY_OTEL_OTLP_URL, defaultIngestUrl); + + if (ingestUrl.startsWith("https://ingest.") + && ingestUrl.endsWith(".observability.splunkcloud.com")) { + logger.log( + WARNING, + "Profiling data can not be sent to {0}, using {1} instead. " + + "You can override it by setting " + + CONFIG_KEY_INGEST_URL, + new Object[] {ingestUrl, defaultIngestUrl}); + return defaultIngestUrl; + } + + if ("http/protobuf".equals(otlpProtocol)) { + ingestUrl = maybeAppendHttpPath(ingestUrl); + } + } + return ingestUrl; + } + + private static String maybeAppendHttpPath(String ingestUrl) { + if (!ingestUrl.endsWith("v1/logs")) { + if (!ingestUrl.endsWith("/")) { + ingestUrl += "/"; + } + ingestUrl += "v1/logs"; + } + return ingestUrl; + } + + private static String getDefaultLogsEndpoint(String otlpProtocol) { + return "http/protobuf".equals(otlpProtocol) + ? "http://localhost:4318/v1/logs" + : "http://localhost:4317"; + } + + private static String getOtlpProtocol(ConfigProperties config) { + return config.getString( + CONFIG_KEY_PROFILER_OTLP_PROTOCOL, + config.getString(CONFIG_KEY_OTLP_PROTOCOL, "http/protobuf")); + } +} diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilingSupervisor.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilingSupervisor.java index c1575c40c..2b9269ffd 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilingSupervisor.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilingSupervisor.java @@ -41,7 +41,7 @@ public class ProfilingSupervisor { private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(ProfilingSupervisor.class.getName()); - private final ProfilerConfiguration config; + private final OptionalConfigurableSupplier configSupplier; private final JFR jfr; private final AutoConfiguredOpenTelemetrySdk sdk; private final BlockingQueue commandQueue; @@ -53,24 +53,24 @@ public class ProfilingSupervisor { @VisibleForTesting ProfilingSupervisor( - ProfilerConfiguration config, + OptionalConfigurableSupplier configSupplier, JFR jfr, AutoConfiguredOpenTelemetrySdk sdk, BlockingQueue commandQueue) { - this.config = config; + this.configSupplier = configSupplier; this.jfr = jfr; this.sdk = sdk; this.commandQueue = commandQueue; } - static ProfilingSupervisor createAndStart( - AutoConfiguredOpenTelemetrySdk sdk, ProfilerConfiguration config) { + static ProfilingSupervisor createAndStart(AutoConfiguredOpenTelemetrySdk sdk) { if (SUPPLIER.isConfigured()) { throw new IllegalStateException("Already started"); } ExecutorService executor = HelpfulExecutors.newSingleThreadExecutor("JFR Profiler"); BlockingQueue queue = new LinkedBlockingQueue<>(); - ProfilingSupervisor supervisor = new ProfilingSupervisor(config, JFR.getInstance(), sdk, queue); + ProfilingSupervisor supervisor = + new ProfilingSupervisor(ProfilerConfiguration.SUPPLIER, JFR.getInstance(), sdk, queue); SUPPLIER.configure(supervisor); supervisor.start(executor); @@ -137,10 +137,10 @@ private void tryStart() { "JDK Flight Recorder (JFR) is not available in this JVM. Profiling will not start."); return; } - config.log(); - logger.info("Profiler is active."); + configSupplier.get().log(); setJfrContextStorageEnabled(true); activateJfrRecording(getResource(sdk)); + logger.info("Profiler is active."); } private void tryStop() { @@ -150,6 +150,7 @@ private void tryStop() { } setJfrContextStorageEnabled(false); deactivateJfrRecording(); + logger.info("Profiler is deactivated."); } private boolean isJfrRecordingActive() { @@ -173,7 +174,7 @@ private void deactivateJfrRecording() { // Exists for testing PeriodicRecordingFlusherBuilder makeRecordingFlusherBuilder(Resource resource) { - return PeriodicRecordingFlusher.builder(config, resource); + return PeriodicRecordingFlusher.builder(configSupplier.get(), resource); } static void setupJfrContextStorage() { diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrAgentListenerTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrAgentListenerTest.java index 9f87f91d0..3d7d28fe2 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrAgentListenerTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrAgentListenerTest.java @@ -43,7 +43,7 @@ class JfrAgentListenerTest { @AfterEach void resetDeclarativeConfigSuppliers() { - ProfilerDeclarativeConfiguration.SUPPLIER.reset(); + ProfilerConfiguration.SUPPLIER.reset(); SnapshotProfilingDeclarativeConfiguration.SUPPLIER.reset(); } @@ -68,8 +68,7 @@ void shouldActivateJfrRecording(@TempDir Path tempDir) throws IOException { JfrAgentListener listener = new JfrAgentListener(jfr) { @Override - ProfilingSupervisor makeProfilingSupervisor( - AutoConfiguredOpenTelemetrySdk sdk, ProfilerConfiguration config) { + ProfilingSupervisor makeProfilingSupervisor(AutoConfiguredOpenTelemetrySdk sdk) { return supervisor; } }; @@ -102,8 +101,7 @@ void shouldNotActivateJfrRecording_JfrNotAvailable(@TempDir Path tempDir) throws JfrAgentListener listener = new JfrAgentListener(jfr) { @Override - ProfilingSupervisor makeProfilingSupervisor( - AutoConfiguredOpenTelemetrySdk sdk, ProfilerConfiguration config) { + ProfilingSupervisor makeProfilingSupervisor(AutoConfiguredOpenTelemetrySdk sdk) { return supervisor; } }; @@ -130,8 +128,7 @@ void shouldNotActivateJfrRecording_profilerDisabled(String yaml, @TempDir Path t JfrAgentListener listener = new JfrAgentListener(jfr) { @Override - ProfilingSupervisor makeProfilingSupervisor( - AutoConfiguredOpenTelemetrySdk sdk, ProfilerConfiguration config) { + ProfilingSupervisor makeProfilingSupervisor(AutoConfiguredOpenTelemetrySdk sdk) { return supervisor; } }; diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/LogExporterBuilderTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/LogExporterBuilderTest.java index 973be8856..b95ec00cd 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/LogExporterBuilderTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/LogExporterBuilderTest.java @@ -53,7 +53,7 @@ class EnvVarBasedConfig { @Test void testBuildSimpleGrpc() { // given - ProfilerEnvVarsConfiguration config = mock(ProfilerEnvVarsConfiguration.class); + ProfilerConfiguration config = mock(ProfilerConfiguration.class); OtlpGrpcLogRecordExporterBuilder builder = mock(OtlpGrpcLogRecordExporterBuilder.class); OtlpGrpcLogRecordExporter expected = mock(OtlpGrpcLogRecordExporter.class); @@ -72,7 +72,7 @@ void testBuildSimpleGrpc() { @Test void testBuildSimpleHttp() { // given - ProfilerEnvVarsConfiguration config = mock(ProfilerEnvVarsConfiguration.class); + ProfilerConfiguration config = mock(ProfilerConfiguration.class); ConfigProperties configProperties = mock(ConfigProperties.class); OtlpHttpLogRecordExporterBuilder builder = mock(OtlpHttpLogRecordExporterBuilder.class); OtlpHttpLogRecordExporter expected = mock(OtlpHttpLogRecordExporter.class); @@ -95,7 +95,7 @@ void testBuildSimpleHttp() { @Test void extraOtlpHeaders() { // given - ProfilerEnvVarsConfiguration config = mock(ProfilerEnvVarsConfiguration.class); + ProfilerConfiguration config = mock(ProfilerConfiguration.class); ConfigProperties configProperties = mock(ConfigProperties.class); when(config.getConfigProperties()).thenReturn(configProperties); when(config.getOtlpProtocol()).thenReturn("http/protobuf"); @@ -127,7 +127,7 @@ void extraOtlpHeaders() { @Test void extraOtlpLogSpecificHeaders() { // given - ProfilerEnvVarsConfiguration config = mock(ProfilerEnvVarsConfiguration.class); + ProfilerConfiguration config = mock(ProfilerConfiguration.class); ConfigProperties configProperties = mock(ConfigProperties.class); when(config.getConfigProperties()).thenReturn(configProperties); when(config.getOtlpProtocol()).thenReturn("http/protobuf"); @@ -173,7 +173,7 @@ void shouldCreateHttpExporter() { DeclarativeConfigProperties exporterConfig = getExporterConfig(model); // when - LogRecordExporter exporter = LogExporterBuilder.fromConfig(exporterConfig); + LogRecordExporter exporter = LogExporterBuilder.fromDeclarativeConfig(exporterConfig); // then assertThat(exporter).isNotNull(); @@ -197,7 +197,7 @@ void shouldCreateGrpcExporter() { DeclarativeConfigProperties exporterConfig = getExporterConfig(model); // when - LogRecordExporter exporter = LogExporterBuilder.fromConfig(exporterConfig); + LogRecordExporter exporter = LogExporterBuilder.fromDeclarativeConfig(exporterConfig); // then assertThat(exporter).isNotNull(); @@ -220,7 +220,7 @@ void shouldCreateHttpExporter_defaultEndpoint() { DeclarativeConfigProperties exporterConfig = getExporterConfig(model); // when - LogRecordExporter exporter = LogExporterBuilder.fromConfig(exporterConfig); + LogRecordExporter exporter = LogExporterBuilder.fromDeclarativeConfig(exporterConfig); // then assertThat(exporter).isNotNull(); @@ -243,7 +243,7 @@ void shouldCreateGrpcExporter_defaultEndpoint() { DeclarativeConfigProperties exporterConfig = getExporterConfig(model); // when - LogRecordExporter exporter = LogExporterBuilder.fromConfig(exporterConfig); + LogRecordExporter exporter = LogExporterBuilder.fromDeclarativeConfig(exporterConfig); // then assertThat(exporter).isNotNull(); @@ -266,7 +266,7 @@ void shouldThrowExceptionForInvalidProtocol() { DeclarativeConfigProperties exporterConfig = getExporterConfig(model); // when, then - assertThatThrownBy(() -> LogExporterBuilder.fromConfig(exporterConfig)) + assertThatThrownBy(() -> LogExporterBuilder.fromDeclarativeConfig(exporterConfig)) .isInstanceOf(ConfigurationException.class); } diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/OtelLoggerFactoryTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/OtelLoggerFactoryTest.java index 67ceaa988..a75309a9c 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/OtelLoggerFactoryTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/OtelLoggerFactoryTest.java @@ -33,7 +33,7 @@ class OtelLoggerFactoryTest { private final InMemoryLogRecordExporter exporter = InMemoryLogRecordExporter.create(); private final OtelLoggerFactory factory = - new OtelLoggerFactory(properties -> exporter, declarativeConfigProperties -> exporter); + new OtelLoggerFactory(() -> exporter, declarativeConfigProperties -> exporter); @Test void configureLoggerWithProfilingInstrumentationScopeName() { diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/PeriodicRecordingFlusherBuilderTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/PeriodicRecordingFlusherBuilderTest.java index d309ac1da..0ab6b23f0 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/PeriodicRecordingFlusherBuilderTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/PeriodicRecordingFlusherBuilderTest.java @@ -17,16 +17,18 @@ package com.splunk.opentelemetry.profiler; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockConstruction; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; import io.opentelemetry.sdk.resources.Resource; import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; +import java.util.Map; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.mockito.MockedConstruction; @@ -35,12 +37,17 @@ class PeriodicRecordingFlusherBuilderTest { @TempDir Path tempDir; + @AfterEach + void tearDown() { + ProfilerConfiguration.SUPPLIER.reset(); + } + @Test void buildConfiguresJfrAndWiresRecorderIntoSequencer() { JFR jfr = mock(JFR.class); - TestProfilingConfig config = config(tempDir); - config.stackDepth = 73; - config.recordingDuration = Duration.ofMillis(100); + ProfilerConfiguration config = + config(tempDir).setStackDepth(73).setRecordingDuration(Duration.ofMillis(100)).build(); + ProfilerConfiguration.SUPPLIER.configure(config); try (MockedConstruction recorderConstruction = mockConstruction(JfrRecorder.class)) { @@ -64,8 +71,8 @@ void buildConfiguresJfrAndWiresRecorderIntoSequencer() { void buildCreatesMissingOutputDirectoryWhenKeepingFiles() { Path outputDir = tempDir.resolve("profiler-output"); JFR jfr = mock(JFR.class); - TestProfilingConfig config = config(outputDir); - config.keepFiles = true; + ProfilerConfiguration config = config(outputDir).setKeepFiles(true).build(); + ProfilerConfiguration.SUPPLIER.configure(config); try (MockedConstruction recorderConstruction = mockConstruction(JfrRecorder.class)) { @@ -83,8 +90,8 @@ void buildContinuesWhenKeepFilesPathIsNotADirectory() throws Exception { Path outputFile = tempDir.resolve("profiler-output"); Files.createFile(outputFile); JFR jfr = mock(JFR.class); - TestProfilingConfig config = config(outputFile); - config.keepFiles = true; + ProfilerConfiguration config = config(outputFile).setKeepFiles(true).build(); + ProfilerConfiguration.SUPPLIER.configure(config); try (MockedConstruction recorderConstruction = mockConstruction(JfrRecorder.class)) { @@ -96,21 +103,19 @@ void buildContinuesWhenKeepFilesPathIsNotADirectory() throws Exception { } } - @Test - void buildRejectsUnsupportedConfigProperties() { - JFR jfr = mock(JFR.class); - TestProfilingConfig config = config(tempDir); - config.configProperties = new Object(); - - assertThatThrownBy( - () -> PeriodicRecordingFlusher.builder(config, Resource.empty()).jfr(jfr).build()) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageStartingWith("Unsupported config properties type:"); - } - - private TestProfilingConfig config(Path outputDir) { - TestProfilingConfig config = new TestProfilingConfig(); - config.profilerDirectory = outputDir.toString(); - return config; + private ProfilerConfiguration.Builder config(Path outputDir) { + return ProfilerConfiguration.builder() + .setEnabled(true) + .setIngestUrl("http://localhost:4318/v1/logs") + .setOtlpProtocol("http/protobuf") + .setProfilerDirectory(outputDir.toString()) + .setRecordingDuration(Duration.ofDays(1)) + .setConfigProperties( + DefaultConfigProperties.createFromMap( + Map.of( + "otel.exporter.otlp.protocol", + "http/protobuf", + "otel.exporter.otlp.endpoint", + "http://localhost:4318"))); } } diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationTest.java new file mode 100644 index 000000000..449b0ba24 --- /dev/null +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationTest.java @@ -0,0 +1,117 @@ +/* + * Copyright Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.splunk.opentelemetry.profiler; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Duration; +import org.junit.jupiter.api.Test; + +class ProfilerConfigurationTest { + + @Test + void toBuilder_shouldCopyExistingConfigurationWithoutMutation() { + Object configProperties = new Object(); + ProfilerConfiguration original = + ProfilerConfiguration.builder() + .setEnabled(true) + .setIngestUrl("https://logs.example.com") + .setOtlpProtocol("grpc") + .setMemoryEnabled(true) + .setMemoryEventRateLimitEnabled(false) + .setMemoryEventRate("250/s") + .setUseAllocationSampleEvent(true) + .setCallStackInterval(Duration.ofMillis(1410)) + .setIncludeAgentInternalStacks(true) + .setIncludeJvmInternalStacks(true) + .setTracingStacksOnly(true) + .setStackDepth(73) + .setKeepFiles(true) + .setProfilerDirectory("/tmp/profiler") + .setRecordingDuration(Duration.ofSeconds(30)) + .setConfigProperties(configProperties) + .build(); + + ProfilerConfiguration copy = original.toBuilder().build(); + + assertThat(copy).isNotSameAs(original); + assertThat(copy).usingRecursiveComparison().isEqualTo(original); + assertThat(copy.getConfigProperties()).isSameAs(configProperties); + } + + @Test + void toBuilder_shouldCopyExistingConfigurationAndAllowMutation() { + Object configProperties = new Object(); + Object mutatedConfigProperties = new Object(); + ProfilerConfiguration original = + ProfilerConfiguration.builder() + .setEnabled(false) + .setIngestUrl("https://logs.example.com") + .setOtlpProtocol("grpc") + .setMemoryEnabled(true) + .setMemoryEventRateLimitEnabled(false) + .setMemoryEventRate("250/s") + .setUseAllocationSampleEvent(true) + .setCallStackInterval(Duration.ofMillis(1410)) + .setIncludeAgentInternalStacks(true) + .setIncludeJvmInternalStacks(true) + .setTracingStacksOnly(true) + .setStackDepth(73) + .setKeepFiles(true) + .setProfilerDirectory("/tmp/profiler") + .setRecordingDuration(Duration.ofSeconds(30)) + .setConfigProperties(configProperties) + .build(); + + ProfilerConfiguration copy = + original.toBuilder() + .setEnabled(true) + .setIngestUrl("https://mutated-logs.example.com") + .setOtlpProtocol("http/protobuf") + .setMemoryEnabled(false) + .setMemoryEventRateLimitEnabled(true) + .setMemoryEventRate("333/s") + .setUseAllocationSampleEvent(false) + .setCallStackInterval(Duration.ofMillis(2500)) + .setIncludeAgentInternalStacks(false) + .setIncludeJvmInternalStacks(false) + .setTracingStacksOnly(false) + .setStackDepth(142) + .setKeepFiles(false) + .setProfilerDirectory("/tmp/mutated-profiler") + .setRecordingDuration(Duration.ofSeconds(60)) + .setConfigProperties(mutatedConfigProperties) + .build(); + + assertThat(copy.isEnabled()).isTrue(); + assertThat(copy.getIngestUrl()).isEqualTo("https://mutated-logs.example.com"); + assertThat(copy.getOtlpProtocol()).isEqualTo("http/protobuf"); + assertThat(copy.getMemoryEnabled()).isFalse(); + assertThat(copy.getMemoryEventRateLimitEnabled()).isTrue(); + assertThat(copy.getMemoryEventRate()).isEqualTo("333/s"); + assertThat(copy.getUseAllocationSampleEvent()).isFalse(); + assertThat(copy.getCallStackInterval()).isEqualTo(Duration.ofMillis(2500)); + assertThat(copy.getIncludeAgentInternalStacks()).isFalse(); + assertThat(copy.getIncludeJvmInternalStacks()).isFalse(); + assertThat(copy.getTracingStacksOnly()).isFalse(); + assertThat(copy.getStackDepth()).isEqualTo(142); + assertThat(copy.getKeepFiles()).isFalse(); + assertThat(copy.getProfilerDirectory()).isEqualTo("/tmp/mutated-profiler"); + assertThat(copy.getRecordingDuration()).isEqualTo(Duration.ofSeconds(60)); + assertThat(copy.getConfigProperties()).isSameAs(mutatedConfigProperties); + } +} diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerDeclarativeConfigurationTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerDeclarativeConfigurationFactoryTest.java similarity index 71% rename from profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerDeclarativeConfigurationTest.java rename to profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerDeclarativeConfigurationFactoryTest.java index 156052a18..a80b4a767 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerDeclarativeConfigurationTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerDeclarativeConfigurationFactoryTest.java @@ -18,7 +18,6 @@ import static com.splunk.opentelemetry.testing.declarativeconfig.DeclarativeConfigTestUtil.getProfilingConfig; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; import com.splunk.opentelemetry.testing.declarativeconfig.DeclarativeConfigTestUtil; import io.opentelemetry.api.incubator.config.DeclarativeConfigProperties; @@ -26,11 +25,10 @@ import java.time.Duration; import org.junit.jupiter.api.Test; -class ProfilerDeclarativeConfigurationTest { +class ProfilerDeclarativeConfigurationFactoryTest { @Test void shouldMapYamlToConfiguration() { - // given OpenTelemetryConfigurationModel model = DeclarativeConfigTestUtil.parse( """ @@ -56,10 +54,8 @@ void shouldMapYamlToConfiguration() { DeclarativeConfigProperties profilingConfig = getProfilingConfig(model); - // when - ProfilerDeclarativeConfiguration config = new ProfilerDeclarativeConfiguration(profilingConfig); + ProfilerConfiguration config = ProfilerDeclarativeConfigurationFactory.create(profilingConfig); - // then assertThat(config.isEnabled()).isTrue(); assertThat(config.getIncludeAgentInternalStacks()).isTrue(); assertThat(config.getIncludeJvmInternalStacks()).isTrue(); @@ -72,9 +68,29 @@ void shouldMapYamlToConfiguration() { assertThat(config.getMemoryEnabled()).isTrue(); assertThat(config.getMemoryEventRateLimitEnabled()).isTrue(); assertThat(config.getMemoryEventRate()).isEqualTo("250/s"); - assertThat(config.getUseAllocationSampleEvent()).isTrue(); + assertThat(config.getUseAllocationSampleEvent()) + .isEqualTo(ProfilerConfiguration.HAS_OBJECT_ALLOCATION_SAMPLE_EVENT); + assertThat(config.getConfigProperties()).isSameAs(profilingConfig); + } + + @Test + void shouldDisableProfilerWhenAlwaysOnIsMissing() { + OpenTelemetryConfigurationModel model = + DeclarativeConfigTestUtil.parse( + """ + file_format: "1.0" + distribution: + splunk: + profiling: + """); + + ProfilerConfiguration config = + ProfilerDeclarativeConfigurationFactory.create(getProfilingConfig(model)); - assertThrows(UnsupportedOperationException.class, config::getIngestUrl); - assertThrows(UnsupportedOperationException.class, config::getOtlpProtocol); + assertThat(config.isEnabled()).isFalse(); + assertThat(config.getMemoryEnabled()).isFalse(); + assertThat(config.getMemoryEventRateLimitEnabled()).isFalse(); + assertThat(config.getCallStackInterval()).isEqualTo(Duration.ofSeconds(10)); + assertThat(config.getRecordingDuration()).isEqualTo(Duration.ofSeconds(20)); } } diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerEnvVarsConfigurationFactoryTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerEnvVarsConfigurationFactoryTest.java new file mode 100644 index 000000000..c65c2731f --- /dev/null +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerEnvVarsConfigurationFactoryTest.java @@ -0,0 +1,182 @@ +/* + * Copyright Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.splunk.opentelemetry.profiler; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import io.opentelemetry.common.ComponentLoader; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; +import java.time.Duration; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class ProfilerEnvVarsConfigurationFactoryTest { + private static final ComponentLoader COMPONENT_LOADER = + ComponentLoader.forClassLoader( + ProfilerEnvVarsConfigurationFactoryTest.class.getClassLoader()); + + String logsEndpoint = "http://logs.example.com"; + String otelEndpoint = "http://otel.example.com"; + String defaultLogsEndpoint = "http://localhost:4318/v1/logs"; + + @Test + void shouldMapPropertiesToConfiguration() { + ConfigProperties configProperties = + config( + Map.ofEntries( + Map.entry("splunk.profiler.enabled", "true"), + Map.entry("splunk.profiler.directory", "/tmp/prof"), + Map.entry("splunk.profiler.recording.duration", "12345ms"), + Map.entry("splunk.profiler.keep-files", "true"), + Map.entry("splunk.profiler.logs-endpoint", logsEndpoint), + Map.entry("splunk.profiler.otlp.protocol", "grpc"), + Map.entry("splunk.profiler.memory.enabled", "true"), + Map.entry("splunk.profiler.memory.event.rate-limit.enabled", "true"), + Map.entry("splunk.profiler.memory.event.rate", "250/s"), + Map.entry("splunk.profiler.memory.native.sampling", "true"), + Map.entry("splunk.profiler.call.stack.interval", "1410ms"), + Map.entry("splunk.profiler.include.agent.internals", "true"), + Map.entry("splunk.profiler.include.jvm.internals", "true"), + Map.entry("splunk.profiler.tracing.stacks.only", "true"), + Map.entry("splunk.profiler.max.stack.depth", "73"))); + + ProfilerConfiguration profilerConfiguration = + ProfilerEnvVarsConfigurationFactory.create(configProperties); + + assertThat(profilerConfiguration.isEnabled()).isTrue(); + assertThat(profilerConfiguration.getIngestUrl()).isEqualTo(logsEndpoint); + assertThat(profilerConfiguration.getOtlpProtocol()).isEqualTo("grpc"); + assertThat(profilerConfiguration.getMemoryEnabled()).isTrue(); + assertThat(profilerConfiguration.getMemoryEventRateLimitEnabled()).isTrue(); + assertThat(profilerConfiguration.getMemoryEventRate()).isEqualTo("250/s"); + assertThat(profilerConfiguration.getUseAllocationSampleEvent()) + .isEqualTo(ProfilerConfiguration.HAS_OBJECT_ALLOCATION_SAMPLE_EVENT); + assertThat(profilerConfiguration.getCallStackInterval()).isEqualTo(Duration.ofMillis(1410)); + assertThat(profilerConfiguration.getIncludeAgentInternalStacks()).isTrue(); + assertThat(profilerConfiguration.getIncludeJvmInternalStacks()).isTrue(); + assertThat(profilerConfiguration.getTracingStacksOnly()).isTrue(); + assertThat(profilerConfiguration.getStackDepth()).isEqualTo(73); + assertThat(profilerConfiguration.getKeepFiles()).isTrue(); + assertThat(profilerConfiguration.getProfilerDirectory()).isEqualTo("/tmp/prof"); + assertThat(profilerConfiguration.getRecordingDuration()).isEqualTo(Duration.ofMillis(12345)); + assertThat(profilerConfiguration.getConfigProperties()).isSameAs(configProperties); + } + + @Test + void getIngestUrl_endpointDefined() { + ProfilerConfiguration profilerConfiguration = + ProfilerEnvVarsConfigurationFactory.create( + config( + Map.of(ProfilerEnvVarsConfigurationFactory.CONFIG_KEY_INGEST_URL, logsEndpoint))); + + assertThat(profilerConfiguration.getIngestUrl()).isEqualTo(logsEndpoint); + } + + @Test + void getIngestUrl_endpointNotDefined_usedOtelGrpc() { + ProfilerConfiguration profilerConfiguration = + ProfilerEnvVarsConfigurationFactory.create( + config( + Map.of( + ProfilerEnvVarsConfigurationFactory.CONFIG_KEY_OTEL_OTLP_URL, + otelEndpoint, + ProfilerEnvVarsConfigurationFactory.CONFIG_KEY_PROFILER_OTLP_PROTOCOL, + "grpc"))); + + assertThat(profilerConfiguration.getIngestUrl()).isEqualTo(otelEndpoint); + } + + @Test + void getIngestUrl_endpointNotDefined_usedOtelHttpProtobuf() { + ProfilerConfiguration profilerConfiguration = + ProfilerEnvVarsConfigurationFactory.create( + config( + Map.of( + ProfilerEnvVarsConfigurationFactory.CONFIG_KEY_OTEL_OTLP_URL, + otelEndpoint, + ProfilerEnvVarsConfigurationFactory.CONFIG_KEY_PROFILER_OTLP_PROTOCOL, + "http/protobuf"))); + + assertThat(profilerConfiguration.getIngestUrl()).isEqualTo(otelEndpoint + "/v1/logs"); + } + + @Test + void getIngestUrl_endpointNotDefined_usedOtelHttpProtobufWithPath() { + String endpoint = otelEndpoint + "/v1/logs"; + + ProfilerConfiguration profilerConfiguration = + ProfilerEnvVarsConfigurationFactory.create( + config( + Map.of( + ProfilerEnvVarsConfigurationFactory.CONFIG_KEY_OTEL_OTLP_URL, + endpoint, + ProfilerEnvVarsConfigurationFactory.CONFIG_KEY_PROFILER_OTLP_PROTOCOL, + "http/protobuf"))); + + assertThat(profilerConfiguration.getIngestUrl()).isEqualTo(endpoint); + } + + @Test + void getIngestUrlSplunkRealm() { + ProfilerConfiguration profilerConfiguration = + ProfilerEnvVarsConfigurationFactory.create( + config( + Map.of( + ProfilerEnvVarsConfigurationFactory.CONFIG_KEY_OTEL_OTLP_URL, + "https://ingest.us0.observability.splunkcloud.com", + ProfilerEnvVarsConfigurationFactory.CONFIG_KEY_PROFILER_OTLP_PROTOCOL, + "http/protobuf"))); + + assertThat(profilerConfiguration.getIngestUrl()).isEqualTo(defaultLogsEndpoint); + } + + @Test + void getOtlpProtocolDefault() { + ProfilerConfiguration profilerConfiguration = + ProfilerEnvVarsConfigurationFactory.create(config(Collections.emptyMap())); + + assertThat(profilerConfiguration.getOtlpProtocol()).isEqualTo("http/protobuf"); + } + + @Test + void getOtlpProtocolOtelPropertySet() { + ProfilerConfiguration profilerConfiguration = + ProfilerEnvVarsConfigurationFactory.create( + config(Collections.singletonMap("otel.exporter.otlp.protocol", "test"))); + + assertThat(profilerConfiguration.getOtlpProtocol()).isEqualTo("test"); + } + + @Test + void getOtlpProtocol() { + Map map = new HashMap<>(); + map.put("otel.exporter.otlp.protocol", "test1"); + map.put("splunk.profiler.otlp.protocol", "test2"); + + ProfilerConfiguration profilerConfiguration = + ProfilerEnvVarsConfigurationFactory.create(config(map)); + + assertThat(profilerConfiguration.getOtlpProtocol()).isEqualTo("test2"); + } + + private static ConfigProperties config(Map map) { + return DefaultConfigProperties.create(map, COMPONENT_LOADER); + } +} diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerEnvVarsConfigurationTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerEnvVarsConfigurationTest.java deleted file mode 100644 index ff397b48b..000000000 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerEnvVarsConfigurationTest.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.splunk.opentelemetry.profiler; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import io.opentelemetry.common.ComponentLoader; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import org.junit.jupiter.api.Test; - -class ProfilerEnvVarsConfigurationTest { - private static final ComponentLoader COMPONENT_LOADER = - ComponentLoader.forClassLoader(ProfilerEnvVarsConfigurationTest.class.getClassLoader()); - - String logsEndpoint = "http://logs.example.com"; - String otelEndpoint = "http://otel.example.com"; - String defaultLogsEndpoint = "http://localhost:4318/v1/logs"; - - @Test - void getIngestUrl_endpointDefined() { - // given - ConfigProperties config = mock(ConfigProperties.class); - ProfilerEnvVarsConfiguration profilerConfiguration = new ProfilerEnvVarsConfiguration(config); - when(config.getString(ProfilerEnvVarsConfiguration.CONFIG_KEY_INGEST_URL)) - .thenReturn(logsEndpoint); - - // when - String result = profilerConfiguration.getIngestUrl(); - - // then - assertThat(result).isEqualTo(logsEndpoint); - } - - @Test - void getIngestUrl_endpointNotDefined_usedOtelGrpc() { - // given - ConfigProperties config = mock(ConfigProperties.class); - ProfilerEnvVarsConfiguration profilerConfiguration = new ProfilerEnvVarsConfiguration(config); - when(config.getString(ProfilerEnvVarsConfiguration.CONFIG_KEY_INGEST_URL)).thenReturn(null); - when(config.getString(eq(ProfilerEnvVarsConfiguration.CONFIG_KEY_OTEL_OTLP_URL), anyString())) - .thenReturn(otelEndpoint); - when(config.getString( - eq(ProfilerEnvVarsConfiguration.CONFIG_KEY_PROFILER_OTLP_PROTOCOL), any())) - .thenReturn("grpc"); - - // when - String result = profilerConfiguration.getIngestUrl(); - - // then - assertThat(result).isEqualTo(otelEndpoint); - } - - @Test - void getIngestUrl_endpointNotDefined_usedOtelHttpProtobuf() { - // given - ConfigProperties config = mock(ConfigProperties.class); - ProfilerEnvVarsConfiguration profilerConfiguration = new ProfilerEnvVarsConfiguration(config); - when(config.getString(ProfilerEnvVarsConfiguration.CONFIG_KEY_INGEST_URL)).thenReturn(null); - when(config.getString(eq(ProfilerEnvVarsConfiguration.CONFIG_KEY_OTEL_OTLP_URL), anyString())) - .thenReturn(otelEndpoint); - when(config.getString( - eq(ProfilerEnvVarsConfiguration.CONFIG_KEY_PROFILER_OTLP_PROTOCOL), any())) - .thenReturn("http/protobuf"); - - // when - String result = profilerConfiguration.getIngestUrl(); - - // then - assertThat(result).isEqualTo(otelEndpoint + "/v1/logs"); - } - - @Test - void getIngestUrl_endpointNotDefined_usedOtelHttpProtobufWithPath() { - // given - String endpoint = otelEndpoint + "/v1/logs"; - - ConfigProperties config = mock(ConfigProperties.class); - ProfilerEnvVarsConfiguration profilerConfiguration = new ProfilerEnvVarsConfiguration(config); - when(config.getString(ProfilerEnvVarsConfiguration.CONFIG_KEY_INGEST_URL)).thenReturn(null); - when(config.getString(eq(ProfilerEnvVarsConfiguration.CONFIG_KEY_OTEL_OTLP_URL), anyString())) - .thenReturn(endpoint); - when(config.getString( - eq(ProfilerEnvVarsConfiguration.CONFIG_KEY_PROFILER_OTLP_PROTOCOL), any())) - .thenReturn("http/protobuf"); - - // when - String result = profilerConfiguration.getIngestUrl(); - - // then - assertThat(result).isEqualTo(endpoint); - } - - @Test - void getIngestUrlSplunkRealm() { - // given - ConfigProperties config = mock(ConfigProperties.class); - ProfilerEnvVarsConfiguration profilerConfiguration = new ProfilerEnvVarsConfiguration(config); - when(config.getString(ProfilerEnvVarsConfiguration.CONFIG_KEY_INGEST_URL)).thenReturn(null); - when(config.getString(eq(ProfilerEnvVarsConfiguration.CONFIG_KEY_OTEL_OTLP_URL), anyString())) - .thenReturn("https://ingest.us0.observability.splunkcloud.com"); - when(config.getString( - eq(ProfilerEnvVarsConfiguration.CONFIG_KEY_PROFILER_OTLP_PROTOCOL), any())) - .thenReturn("http/protobuf"); - - // when - String result = profilerConfiguration.getIngestUrl(); - - // then - assertThat(result).isEqualTo(defaultLogsEndpoint); - } - - @Test - void getOtlpProtocolDefault() { - // given - ProfilerEnvVarsConfiguration profilerConfiguration = - new ProfilerEnvVarsConfiguration( - DefaultConfigProperties.create(Collections.emptyMap(), COMPONENT_LOADER)); - - // when - String result = profilerConfiguration.getOtlpProtocol(); - - // then - assertThat(result).isEqualTo("http/protobuf"); - } - - @Test - void getOtlpProtocolOtelPropertySet() { - // given - ProfilerEnvVarsConfiguration profilerConfiguration = - new ProfilerEnvVarsConfiguration( - DefaultConfigProperties.create( - Collections.singletonMap("otel.exporter.otlp.protocol", "test"), COMPONENT_LOADER)); - - // when - String result = profilerConfiguration.getOtlpProtocol(); - - // then - assertThat(result).isEqualTo("test"); - } - - @Test - void getOtlpProtocol() { - // given - Map map = new HashMap<>(); - map.put("otel.exporter.otlp.protocol", "test1"); - map.put("splunk.profiler.otlp.protocol", "test2"); - - ProfilerEnvVarsConfiguration profilerConfiguration = - new ProfilerEnvVarsConfiguration(DefaultConfigProperties.create(map, COMPONENT_LOADER)); - - // when - String result = profilerConfiguration.getOtlpProtocol(); - - // then - assertThat(result).isEqualTo("test2"); - } -} diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilingSupervisorTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilingSupervisorTest.java index 3d2e133e5..1d4a1d0b1 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilingSupervisorTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilingSupervisorTest.java @@ -21,10 +21,12 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.splunk.opentelemetry.profiler.util.OptionalConfigurableSupplier; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import io.opentelemetry.sdk.resources.Resource; import java.nio.file.Path; @@ -45,7 +47,7 @@ class ProfilingSupervisorTest { @Mock JFR jfr; - TestProfilingConfig config; + ProfilerConfiguration config; TestPeriodicRecordingFlusherBuilder builder; AutoConfiguredOpenTelemetrySdk sdk; ExecutorService executor; @@ -53,8 +55,14 @@ class ProfilingSupervisorTest { @BeforeEach void setUp(@TempDir Path tempDir) { - config = new TestProfilingConfig(); - config.profilerDirectory = tempDir.toString(); + config = + spy( + ProfilerConfiguration.builder() + .setEnabled(true) + .setProfilerDirectory(tempDir.toString()) + .setStackDepth(4321) + .setRecordingDuration(Duration.ofMinutes(1)) + .build()); builder = new TestPeriodicRecordingFlusherBuilder(config, mock(Resource.class)); executor = Executors.newSingleThreadExecutor(); sdk = @@ -88,21 +96,19 @@ void requestStartDoesNotStartProfilerWhenJfrIsUnavailable() { supervisor.requestStartProfiling(); await().untilAsserted(() -> verify(jfr).isAvailable()); - assertThat(config.logCalled).isFalse(); + verify(config, never()).log(); verify(jfr, never()).setStackDepth(anyInt()); assertThat(builder.buildCalled).isFalse(); } @Test void requestStartProfilingBuildsAndStartsRecordingSequencer() { - config.stackDepth = 4321; - config.recordingDuration = Duration.ofMinutes(1); when(jfr.isAvailable()).thenReturn(true); supervisor.requestStartProfiling(); await().untilAsserted(() -> assertThat(builder.buildCalled).isTrue()); - assertThat(config.logCalled).isTrue(); + verify(config).log(); assertThat(builder.jfr).isSameAs(jfr); assertThat(builder.config).isSameAs(config); assertThat(builder.resource).isNotNull(); @@ -156,7 +162,7 @@ private static class TestProfilingSupervisor extends ProfilingSupervisor { JFR jfr, AutoConfiguredOpenTelemetrySdk sdk, PeriodicRecordingFlusherBuilder builder) { - super(config, jfr, sdk, new LinkedBlockingQueue<>()); + super(configSupplier(config), jfr, sdk, new LinkedBlockingQueue<>()); this.builder = builder; } @@ -164,6 +170,14 @@ private static class TestProfilingSupervisor extends ProfilingSupervisor { PeriodicRecordingFlusherBuilder makeRecordingFlusherBuilder(Resource resource) { return builder; } + + private static OptionalConfigurableSupplier configSupplier( + ProfilerConfiguration config) { + OptionalConfigurableSupplier supplier = + new OptionalConfigurableSupplier<>(); + supplier.configure(config); + return supplier; + } } private static class TestPeriodicRecordingFlusherBuilder extends PeriodicRecordingFlusherBuilder { diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/TestProfilingConfig.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/TestProfilingConfig.java deleted file mode 100644 index 20cd32291..000000000 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/TestProfilingConfig.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright Splunk Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.splunk.opentelemetry.profiler; - -import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties; -import java.time.Duration; -import java.util.Map; - -class TestProfilingConfig implements ProfilerConfiguration { - - boolean logCalled; - String profilerDirectory; - boolean keepFiles; - int stackDepth = 1024; - Duration recordingDuration = Duration.ofDays(1); - Duration callStackInterval = Duration.ofSeconds(10); - boolean includeAgentInternalStacks; - boolean includeJvmInternalStacks; - boolean tracingStacksOnly; - boolean memoryEnabled; - boolean memoryEventRateLimitEnabled = true; - String memoryEventRate = "150/s"; - boolean useAllocationSampleEvent; - Object configProperties = - DefaultConfigProperties.createFromMap( - Map.of( - "otel.exporter.otlp.protocol", - "http/protobuf", - "otel.exporter.otlp.endpoint", - "http://localhost:4318")); - - @Override - public boolean isEnabled() { - return true; - } - - @Override - public void log() { - logCalled = true; - } - - @Override - public String getIngestUrl() { - return "http://localhost:4318/v1/logs"; - } - - @Override - public String getOtlpProtocol() { - return "http/protobuf"; - } - - @Override - public boolean getMemoryEnabled() { - return memoryEnabled; - } - - @Override - public boolean getMemoryEventRateLimitEnabled() { - return memoryEventRateLimitEnabled; - } - - @Override - public String getMemoryEventRate() { - return memoryEventRate; - } - - @Override - public boolean getUseAllocationSampleEvent() { - return useAllocationSampleEvent; - } - - @Override - public Duration getCallStackInterval() { - return callStackInterval; - } - - @Override - public boolean getIncludeAgentInternalStacks() { - return includeAgentInternalStacks; - } - - @Override - public boolean getIncludeJvmInternalStacks() { - return includeJvmInternalStacks; - } - - @Override - public boolean getTracingStacksOnly() { - return tracingStacksOnly; - } - - @Override - public int getStackDepth() { - return stackDepth; - } - - @Override - public boolean getKeepFiles() { - return keepFiles; - } - - @Override - public String getProfilerDirectory() { - return profilerDirectory; - } - - @Override - public Duration getRecordingDuration() { - return recordingDuration; - } - - @Override - public Object getConfigProperties() { - return configProperties; - } -} diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/ConcurrentServiceEntrySamplingTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/ConcurrentServiceEntrySamplingTest.java index 327e9bc68..70f6a8934 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/ConcurrentServiceEntrySamplingTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/ConcurrentServiceEntrySamplingTest.java @@ -66,7 +66,7 @@ private StackTraceSampler newSampler(StagingArea staging) { .with( new StackTraceExporterActivator( new OtelLoggerFactory( - properties -> logExporter, declarativeConfigProperties -> logExporter))) + () -> logExporter, declarativeConfigProperties -> logExporter))) .build(); @RegisterExtension diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/GracefulShutdownTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/GracefulShutdownTest.java index c54ad34dc..5b80afa7a 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/GracefulShutdownTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/GracefulShutdownTest.java @@ -43,7 +43,7 @@ class GracefulShutdownTest { .with( new StackTraceExporterActivator( new OtelLoggerFactory( - properties -> logExporter, declarativeConfigProperties -> logExporter))) + () -> logExporter, declarativeConfigProperties -> logExporter))) .build(); @Test diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingLogExportingTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingLogExportingTest.java index ee1b18bab..db71b2917 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingLogExportingTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/snapshot/SnapshotProfilingLogExportingTest.java @@ -63,7 +63,7 @@ class SnapshotProfilingLogExportingTest { .with( new StackTraceExporterActivator( new OtelLoggerFactory( - properties -> logExporter, declarativeConfigProperties -> logExporter))) + () -> logExporter, declarativeConfigProperties -> logExporter))) .build(); @AfterEach