From 87f1339b935c8c49d38a3e1861ade4810b9b6321 Mon Sep 17 00:00:00 2001 From: robsunday Date: Wed, 29 Oct 2025 18:09:56 +0100 Subject: [PATCH 01/16] Initial configuration refactoring --- .../opentelemetry/SplunkConfiguration.java | 4 + profiler/build.gradle.kts | 2 + .../opentelemetry/profiler/Configuration.java | 117 ++++++++++-------- .../profiler/ConfigurationLogger.java | 69 ----------- .../opentelemetry/profiler/JfrActivator.java | 20 +-- .../profiler/JfrSettingsOverrides.java | 14 +-- .../opentelemetry/profiler/SdkCustomizer.java | 4 +- .../profiler/ConfigurationLoggerTest.java | 101 --------------- .../profiler/ConfigurationTest.java | 6 +- .../profiler/TLABProcessorTest.java | 9 +- 10 files changed, 89 insertions(+), 257 deletions(-) delete mode 100644 profiler/src/main/java/com/splunk/opentelemetry/profiler/ConfigurationLogger.java delete mode 100644 profiler/src/test/java/com/splunk/opentelemetry/profiler/ConfigurationLoggerTest.java diff --git a/custom/src/main/java/com/splunk/opentelemetry/SplunkConfiguration.java b/custom/src/main/java/com/splunk/opentelemetry/SplunkConfiguration.java index a5e1feb89..f2a7479c0 100644 --- a/custom/src/main/java/com/splunk/opentelemetry/SplunkConfiguration.java +++ b/custom/src/main/java/com/splunk/opentelemetry/SplunkConfiguration.java @@ -123,6 +123,10 @@ public static String getOtlpLogsProtocol(ConfigProperties config) { "otel.exporter.otlp.logs.protocol", config.getString("otel.exporter.otlp.protocol")); } + public static boolean isProfilerEnabled(ConfigProperties config) { + return config.getBoolean(PROFILER_ENABLED_PROPERTY, false); + } + private static void addIfAbsent( Map customized, ConfigProperties config, String key, String value) { if (config.getString(key) == null) { diff --git a/profiler/build.gradle.kts b/profiler/build.gradle.kts index ebe89afc6..0b8d83682 100644 --- a/profiler/build.gradle.kts +++ b/profiler/build.gradle.kts @@ -24,6 +24,7 @@ dependencies { compileOnly("io.opentelemetry:opentelemetry-sdk") compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator") compileOnly("io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api") compileOnly("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api") compileOnly("io.opentelemetry.semconv:opentelemetry-semconv") @@ -46,6 +47,7 @@ dependencies { testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api") testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") + testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator") testImplementation("io.opentelemetry.semconv:opentelemetry-semconv") testImplementation("io.opentelemetry:opentelemetry-context") testImplementation("io.opentelemetry:opentelemetry-api") diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java index fa4cbeca1..8ffe3ac63 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java @@ -20,74 +20,77 @@ import static com.splunk.opentelemetry.SplunkConfiguration.PROFILER_MEMORY_ENABLED_PROPERTY; import static java.util.logging.Level.WARNING; -import com.google.auto.service.AutoService; -import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; -import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; +import com.splunk.opentelemetry.SplunkConfiguration; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.time.Duration; -import java.util.HashMap; -import java.util.Map; +import java.util.function.Function; import java.util.logging.Logger; -@AutoService(AutoConfigurationCustomizerProvider.class) -public class Configuration implements AutoConfigurationCustomizerProvider { +public class Configuration { private static final Logger logger = Logger.getLogger(Configuration.class.getName()); + + /* Keys visible for testing */ + 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_OTLP_PROTOCOL = "otel.exporter.otlp.protocol"; + static final String CONFIG_KEY_OTEL_OTLP_URL = "otel.exporter.otlp.endpoint"; + static final String CONFIG_KEY_MEMORY_ENABLED = PROFILER_MEMORY_ENABLED_PROPERTY; + 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 boolean HAS_OBJECT_ALLOCATION_SAMPLE_EVENT = getJavaVersion() >= 16; - private static final String DEFAULT_RECORDING_DURATION = "20s"; - public static final boolean DEFAULT_MEMORY_ENABLED = false; - public static final Duration DEFAULT_CALL_STACK_INTERVAL = Duration.ofSeconds(10); - public static final boolean DEFAULT_INCLUDE_INTERNAL_STACKS = false; - public static final boolean DEFAULT_TRACING_STACKS_ONLY = false; + private static final Duration DEFAULT_RECORDING_DURATION = Duration.ofSeconds(20); + private static final boolean DEFAULT_MEMORY_ENABLED = false; + private static final Duration DEFAULT_CALL_STACK_INTERVAL = Duration.ofSeconds(10); + private static final boolean DEFAULT_INCLUDE_INTERNAL_STACKS = false; + private static final boolean DEFAULT_TRACING_STACKS_ONLY = false; private static final int DEFAULT_STACK_DEPTH = 1024; private static final boolean DEFAULT_MEMORY_EVENT_RATE_LIMIT_ENABLED = true; - - public static final String CONFIG_KEY_ENABLE_PROFILER = PROFILER_ENABLED_PROPERTY; - 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_OTLP_PROTOCOL = "otel.exporter.otlp.protocol"; - public static final String CONFIG_KEY_OTEL_OTLP_URL = "otel.exporter.otlp.endpoint"; - public static final String CONFIG_KEY_MEMORY_ENABLED = PROFILER_MEMORY_ENABLED_PROPERTY; - public static final String CONFIG_KEY_MEMORY_EVENT_RATE_LIMIT_ENABLED = - "splunk.profiler.memory.event.rate-limit.enabled"; // ObjectAllocationSample event uses 150/s in default and 300/s in profiling configuration private static final String DEFAULT_MEMORY_EVENT_RATE = "150/s"; - 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"; - private static final String CONFIG_KEY_STACK_DEPTH = "splunk.profiler.max.stack.depth"; - @Override - public void customize(AutoConfigurationCustomizer autoConfiguration) { - autoConfiguration.addPropertiesSupplier(this::defaultProperties); + public static void log(ConfigProperties config) { + logger.info("-----------------------"); + logger.info("Profiler configuration:"); + log(PROFILER_ENABLED_PROPERTY, (it) -> SplunkConfiguration.isProfilerEnabled(config)); + log(CONFIG_KEY_PROFILER_DIRECTORY, (it) -> getProfilerDirectory(config)); + log(CONFIG_KEY_RECORDING_DURATION, (it) -> getRecordingDuration(config)); + log(CONFIG_KEY_KEEP_FILES, (it) -> getKeepFiles(config)); + log(CONFIG_KEY_INGEST_URL, (it) -> getConfigUrl(config)); + log(CONFIG_KEY_OTEL_OTLP_URL, config::getString); + log(CONFIG_KEY_MEMORY_ENABLED, (it) -> getMemoryEnabled(config)); + if (getMemoryEventRateLimitEnabled(config)) { + log(CONFIG_KEY_MEMORY_EVENT_RATE, (it) -> getMemoryEventRate(config)); + } + log(CONFIG_KEY_CALL_STACK_INTERVAL, (it) -> getCallStackInterval(config)); + log(CONFIG_KEY_INCLUDE_AGENT_INTERNALS, (it) -> getIncludeAgentInternalStacks(config)); + log(CONFIG_KEY_INCLUDE_JVM_INTERNALS, (it) -> getIncludeJvmInternalStacks(config)); + log(CONFIG_KEY_TRACING_STACKS_ONLY, (it) -> getTracingStacksOnly(config)); + log(CONFIG_KEY_STACK_DEPTH, (it) -> getStackDepth(config)); + logger.info("-----------------------"); } - Map defaultProperties() { - HashMap config = new HashMap<>(); - config.put(CONFIG_KEY_ENABLE_PROFILER, "false"); - config.put(CONFIG_KEY_PROFILER_DIRECTORY, System.getProperty("java.io.tmpdir")); - config.put(CONFIG_KEY_RECORDING_DURATION, DEFAULT_RECORDING_DURATION); - config.put(CONFIG_KEY_KEEP_FILES, "false"); - config.put(CONFIG_KEY_MEMORY_ENABLED, String.valueOf(DEFAULT_MEMORY_ENABLED)); - config.put(CONFIG_KEY_MEMORY_EVENT_RATE, DEFAULT_MEMORY_EVENT_RATE); - config.put(CONFIG_KEY_CALL_STACK_INTERVAL, DEFAULT_CALL_STACK_INTERVAL.toMillis() + "ms"); - return config; + private static void log(String key, Function getter) { + logger.info(String.format("%39s : %s", key, getter.apply(key))); } public static String getConfigUrl(ConfigProperties config) { - String ingestUrl = config.getString(CONFIG_KEY_OTEL_OTLP_URL, null); + String ingestUrl = config.getString(CONFIG_KEY_OTEL_OTLP_URL); if (ingestUrl != null) { if (ingestUrl.startsWith("https://ingest.") && ingestUrl.endsWith(".signalfx.com") @@ -164,6 +167,18 @@ public static int getStackDepth(ConfigProperties config) { return config.getInt(CONFIG_KEY_STACK_DEPTH, DEFAULT_STACK_DEPTH); } + public static boolean getKeepFiles(ConfigProperties config) { + return config.getBoolean(CONFIG_KEY_KEEP_FILES, false); + } + + public static String getProfilerDirectory(ConfigProperties config) { + return config.getString(CONFIG_KEY_PROFILER_DIRECTORY, System.getProperty("java.io.tmpdir")); + } + + public static Duration getRecordingDuration(ConfigProperties config) { + return config.getDuration(CONFIG_KEY_RECORDING_DURATION, DEFAULT_RECORDING_DURATION); + } + private static int getJavaVersion() { String javaSpecVersion = System.getProperty("java.specification.version"); if ("1.8".equals(javaSpecVersion)) { diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ConfigurationLogger.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ConfigurationLogger.java deleted file mode 100644 index 202dded6e..000000000 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ConfigurationLogger.java +++ /dev/null @@ -1,69 +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 com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_CALL_STACK_INTERVAL; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_ENABLE_PROFILER; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_INCLUDE_INTERNAL_STACKS; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_INGEST_URL; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_KEEP_FILES; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_MEMORY_ENABLED; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_MEMORY_EVENT_RATE; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_OTEL_OTLP_URL; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_PROFILER_DIRECTORY; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_RECORDING_DURATION; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_TRACING_STACKS_ONLY; -import static com.splunk.opentelemetry.profiler.Configuration.DEFAULT_INCLUDE_INTERNAL_STACKS; - -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import java.util.function.Function; -import java.util.logging.Logger; - -/** This class logs the active profiler configuration for debug/troubleshooting purposes. */ -public class ConfigurationLogger { - - private static final Logger logger = Logger.getLogger(ConfigurationLogger.class.getName()); - - public void log(ConfigProperties config) { - logger.info("-----------------------"); - logger.info("Profiler configuration:"); - log(CONFIG_KEY_ENABLE_PROFILER, (it) -> config.getBoolean(it, false)); - log(CONFIG_KEY_PROFILER_DIRECTORY, config::getString); - log(CONFIG_KEY_RECORDING_DURATION, config::getString); - log(CONFIG_KEY_KEEP_FILES, (it) -> config.getBoolean(it, false)); - log(CONFIG_KEY_INGEST_URL, (it) -> Configuration.getConfigUrl(config)); - log(CONFIG_KEY_OTEL_OTLP_URL, (it) -> config.getString(it, null)); - log(CONFIG_KEY_MEMORY_ENABLED, (it) -> Configuration.getMemoryEnabled(config)); - if (Configuration.getMemoryEventRateLimitEnabled(config)) { - log(CONFIG_KEY_MEMORY_EVENT_RATE, (it) -> Configuration.getMemoryEventRate(config)); - } - log(CONFIG_KEY_CALL_STACK_INTERVAL, (it) -> Configuration.getCallStackInterval(config)); - log( - CONFIG_KEY_INCLUDE_INTERNAL_STACKS, - (it) -> config.getBoolean(it, DEFAULT_INCLUDE_INTERNAL_STACKS)); - log(CONFIG_KEY_TRACING_STACKS_ONLY, (it) -> Configuration.getTracingStacksOnly(config)); - logger.info("-----------------------"); - } - - private void log(String key, Function getter) { - logger.info(" " + pad(key) + " : " + getter.apply(key)); - } - - private String pad(String str) { - return String.format("%39s", str); - } -} diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java index 8a93d9702..41b3bc9ba 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java @@ -16,16 +16,13 @@ package com.splunk.opentelemetry.profiler; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_ENABLE_PROFILER; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_KEEP_FILES; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_PROFILER_DIRECTORY; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_RECORDING_DURATION; import static com.splunk.opentelemetry.profiler.util.Runnables.logUncaught; import static io.opentelemetry.sdk.autoconfigure.AutoConfigureUtil.getConfig; import static io.opentelemetry.sdk.autoconfigure.AutoConfigureUtil.getResource; import static java.util.logging.Level.WARNING; import com.google.auto.service.AutoService; +import com.splunk.opentelemetry.SplunkConfiguration; import com.splunk.opentelemetry.profiler.allocation.exporter.AllocationEventExporter; import com.splunk.opentelemetry.profiler.allocation.exporter.PprofAllocationEventExporter; import com.splunk.opentelemetry.profiler.context.SpanContextualizer; @@ -55,7 +52,6 @@ public class JfrActivator implements AgentListener { private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(JfrActivator.class.getName()); private final ExecutorService executor = HelpfulExecutors.newSingleThreadExecutor("JFR Profiler"); - private final ConfigurationLogger configurationLogger = new ConfigurationLogger(); @Override public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) { @@ -64,7 +60,7 @@ public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetr return; } - configurationLogger.log(config); + Configuration.log(config); logger.info("Profiler is active."); executor.submit( logUncaught( @@ -72,7 +68,7 @@ public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetr } private boolean notClearForTakeoff(ConfigProperties config) { - if (!config.getBoolean(CONFIG_KEY_ENABLE_PROFILER, false)) { + if (!SplunkConfiguration.isProfilerEnabled(config)) { logger.fine("Profiler is not enabled."); return true; } @@ -113,8 +109,8 @@ private void outdirWarn(Path dir, String suffix) { } private void activateJfrAndRunForever(ConfigProperties config, Resource resource) { - boolean keepFiles = keepFiles(config); - Path outputDir = Paths.get(config.getString(CONFIG_KEY_PROFILER_DIRECTORY)); + boolean keepFiles = Configuration.getKeepFiles(config); + Path outputDir = Paths.get(Configuration.getProfilerDirectory(config)); if (keepFiles && !checkOutputDir(outputDir)) { keepFiles = false; } @@ -124,7 +120,7 @@ private void activateJfrAndRunForever(ConfigProperties config, Resource resource JFR.instance.setStackDepth(stackDepth); // can't be null, default value is set in Configuration.getProperties - Duration recordingDuration = config.getDuration(CONFIG_KEY_RECORDING_DURATION, null); + Duration recordingDuration = Configuration.getRecordingDuration(config); Map jfrSettings = buildJfrSettings(config); EventReader eventReader = new EventReader(); @@ -222,8 +218,4 @@ private Map buildJfrSettings(ConfigProperties config) { JfrSettingsOverrides overrides = new JfrSettingsOverrides(config); return overrides.apply(jfrSettings); } - - private boolean keepFiles(ConfigProperties config) { - return config.getBoolean(CONFIG_KEY_KEEP_FILES, false); - } } diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrSettingsOverrides.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrSettingsOverrides.java index 80a948447..ba2b2502e 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrSettingsOverrides.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrSettingsOverrides.java @@ -16,8 +16,6 @@ package com.splunk.opentelemetry.profiler; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_CALL_STACK_INTERVAL; - import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.time.Duration; import java.util.HashMap; @@ -38,21 +36,13 @@ class JfrSettingsOverrides { Map apply(Map jfrSettings) { Map settings = new HashMap<>(jfrSettings); - Duration customInterval = getCustomInterval(); - if (customInterval != Duration.ZERO) { + Duration customInterval = Configuration.getCallStackInterval(config); + if (!Duration.ZERO.equals(customInterval)) { settings.put("jdk.ThreadDump#period", customInterval.toMillis() + " ms"); } return maybeEnableTLABs(settings); } - private Duration getCustomInterval() { - Duration customInterval = config.getDuration(CONFIG_KEY_CALL_STACK_INTERVAL, Duration.ZERO); - if (customInterval != Duration.ZERO) { - return customInterval; - } - return Duration.ZERO; - } - private Map maybeEnableTLABs(Map settings) { if (Configuration.getMemoryEnabled(config)) { if (Configuration.getMemoryEventRateLimitEnabled(config) diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/SdkCustomizer.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/SdkCustomizer.java index 576102cd9..21c215af0 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/SdkCustomizer.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/SdkCustomizer.java @@ -16,10 +16,10 @@ package com.splunk.opentelemetry.profiler; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_ENABLE_PROFILER; import static java.util.Collections.emptyMap; import com.google.auto.service.AutoService; +import com.splunk.opentelemetry.SplunkConfiguration; import io.opentelemetry.context.ContextStorage; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; @@ -44,6 +44,6 @@ private boolean jfrIsAvailable() { } private boolean jfrIsEnabledInConfig(ConfigProperties config) { - return config.getBoolean(CONFIG_KEY_ENABLE_PROFILER, false); + return SplunkConfiguration.isProfilerEnabled(config); } } diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/ConfigurationLoggerTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/ConfigurationLoggerTest.java deleted file mode 100644 index 192a23d34..000000000 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/ConfigurationLoggerTest.java +++ /dev/null @@ -1,101 +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 com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_CALL_STACK_INTERVAL; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_ENABLE_PROFILER; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_INCLUDE_INTERNAL_STACKS; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_INGEST_URL; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_KEEP_FILES; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_MEMORY_ENABLED; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_OTEL_OTLP_URL; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_PROFILER_DIRECTORY; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_RECORDING_DURATION; -import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_TRACING_STACKS_ONLY; -import static com.splunk.opentelemetry.profiler.Configuration.DEFAULT_CALL_STACK_INTERVAL; -import static com.splunk.opentelemetry.profiler.Configuration.DEFAULT_INCLUDE_INTERNAL_STACKS; -import static com.splunk.opentelemetry.profiler.Configuration.DEFAULT_MEMORY_ENABLED; -import static com.splunk.opentelemetry.profiler.Configuration.DEFAULT_TRACING_STACKS_ONLY; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import io.github.netmikey.logunit.api.LogCapturer; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; -import java.time.Duration; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -class ConfigurationLoggerTest { - - @RegisterExtension - LogCapturer log = LogCapturer.create().captureForType(ConfigurationLogger.class); - - @Test - void testLog() { - ConfigProperties config = mock(ConfigProperties.class); - - when(config.getBoolean(CONFIG_KEY_ENABLE_PROFILER, false)).thenReturn(true); - when(config.getString(CONFIG_KEY_PROFILER_DIRECTORY)).thenReturn("somedir"); - when(config.getString(CONFIG_KEY_RECORDING_DURATION)).thenReturn("33m"); - when(config.getBoolean(CONFIG_KEY_KEEP_FILES, false)).thenReturn(true); - when(config.getString(CONFIG_KEY_OTEL_OTLP_URL, null)).thenReturn("http://otel.example.com"); - when(config.getString(CONFIG_KEY_INGEST_URL, "http://otel.example.com")) - .thenReturn("http://example.com"); - when(config.getBoolean(CONFIG_KEY_MEMORY_ENABLED, DEFAULT_MEMORY_ENABLED)).thenReturn(false); - when(config.getDuration(CONFIG_KEY_CALL_STACK_INTERVAL, DEFAULT_CALL_STACK_INTERVAL)) - .thenReturn(Duration.ofSeconds(21)); - when(config.getBoolean(CONFIG_KEY_INCLUDE_INTERNAL_STACKS, DEFAULT_INCLUDE_INTERNAL_STACKS)) - .thenReturn(true); - when(config.getBoolean(CONFIG_KEY_TRACING_STACKS_ONLY, DEFAULT_TRACING_STACKS_ONLY)) - .thenReturn(true); - - ConfigurationLogger configurationLogger = new ConfigurationLogger(); - - configurationLogger.log(config); - - log.assertContains("-----------------------"); - log.assertContains("Profiler configuration:"); - log.assertContains(" splunk.profiler.enabled : true"); - log.assertContains(" splunk.profiler.directory : somedir"); - log.assertContains(" splunk.profiler.recording.duration : 33m"); - log.assertContains(" splunk.profiler.keep-files : true"); - log.assertContains(" splunk.profiler.logs-endpoint : http://example.com"); - log.assertContains(" otel.exporter.otlp.endpoint : http://otel.example.com"); - log.assertContains(" splunk.profiler.memory.enabled : false"); - log.assertContains(" splunk.profiler.call.stack.interval : PT21S"); - log.assertContains("splunk.profiler.include.internal.stacks : true"); - log.assertContains(" splunk.profiler.tracing.stacks.only : true"); - } - - @Test - void testLogInheritDefaultValues() { - ConfigProperties config = mock(ConfigProperties.class); - - String inheritedUrl = "http://otel.example.com"; - when(config.getString(CONFIG_KEY_OTEL_OTLP_URL, null)).thenReturn(inheritedUrl); - when(config.getString(CONFIG_KEY_INGEST_URL, inheritedUrl)).thenReturn(inheritedUrl); - when(config.getBoolean(CONFIG_KEY_MEMORY_ENABLED, DEFAULT_MEMORY_ENABLED)).thenReturn(true); - - ConfigurationLogger configurationLogger = new ConfigurationLogger(); - - configurationLogger.log(config); - - log.assertContains(" splunk.profiler.logs-endpoint : http://otel.example.com"); - log.assertContains(" otel.exporter.otlp.endpoint : http://otel.example.com"); - log.assertContains(" splunk.profiler.memory.enabled : true"); - } -} diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/ConfigurationTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/ConfigurationTest.java index aa2c09f41..b27fe4587 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/ConfigurationTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/ConfigurationTest.java @@ -39,7 +39,7 @@ class ConfigurationTest { @Test void getConfigUrl_endpointDefined() { ConfigProperties config = mock(ConfigProperties.class); - when(config.getString(Configuration.CONFIG_KEY_OTEL_OTLP_URL, null)).thenReturn(otelEndpoint); + when(config.getString(Configuration.CONFIG_KEY_OTEL_OTLP_URL)).thenReturn(otelEndpoint); when(config.getString(Configuration.CONFIG_KEY_INGEST_URL, otelEndpoint)) .thenReturn(logsEndpoint); String result = Configuration.getConfigUrl(config); @@ -49,7 +49,7 @@ void getConfigUrl_endpointDefined() { @Test void getConfigUrl_endpointNotDefined() { ConfigProperties config = mock(ConfigProperties.class); - when(config.getString(Configuration.CONFIG_KEY_OTEL_OTLP_URL, null)).thenReturn(otelEndpoint); + when(config.getString(Configuration.CONFIG_KEY_OTEL_OTLP_URL)).thenReturn(otelEndpoint); when(config.getString(Configuration.CONFIG_KEY_INGEST_URL, otelEndpoint)) .thenReturn(otelEndpoint); String result = Configuration.getConfigUrl(config); @@ -68,7 +68,7 @@ void getConfigUrlNull() { @Test void getConfigUrlSplunkRealm() { ConfigProperties config = mock(ConfigProperties.class); - when(config.getString(Configuration.CONFIG_KEY_OTEL_OTLP_URL, null)) + when(config.getString(Configuration.CONFIG_KEY_OTEL_OTLP_URL)) .thenReturn("https://ingest.us0.signalfx.com"); when(config.getString(Configuration.CONFIG_KEY_INGEST_URL, null)).thenReturn(null); String result = Configuration.getConfigUrl(config); diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/TLABProcessorTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/TLABProcessorTest.java index 62debd485..2a5849683 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/TLABProcessorTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/TLABProcessorTest.java @@ -17,7 +17,6 @@ package com.splunk.opentelemetry.profiler; import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_MEMORY_ENABLED; -import static com.splunk.opentelemetry.profiler.Configuration.DEFAULT_MEMORY_ENABLED; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; @@ -66,7 +65,7 @@ void testNullStack() { when(eventReader.getStackTrace(event)).thenReturn(null); // just to be explicit ConfigProperties config = mock(ConfigProperties.class); - when(config.getBoolean(CONFIG_KEY_MEMORY_ENABLED, DEFAULT_MEMORY_ENABLED)).thenReturn(true); + when(config.getBoolean(CONFIG_KEY_MEMORY_ENABLED, false)).thenReturn(true); TLABProcessor processor = TLABProcessor.builder(config).eventReader(eventReader).build(); processor.accept(event); @@ -87,7 +86,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { }); ConfigProperties config = mock(ConfigProperties.class); - when(config.getBoolean(CONFIG_KEY_MEMORY_ENABLED, DEFAULT_MEMORY_ENABLED)).thenReturn(false); + when(config.getBoolean(CONFIG_KEY_MEMORY_ENABLED, false)).thenReturn(false); TLABProcessor processor = TLABProcessor.builder(config).build(); processor.accept(event); @@ -101,7 +100,7 @@ void testProcess() { IItem event = createMockEvent(serializer, now); ConfigProperties config = mock(ConfigProperties.class); - when(config.getBoolean(CONFIG_KEY_MEMORY_ENABLED, DEFAULT_MEMORY_ENABLED)).thenReturn(true); + when(config.getBoolean(CONFIG_KEY_MEMORY_ENABLED, false)).thenReturn(true); SpanContext spanContext = SpanContext.create( @@ -155,7 +154,7 @@ void testSampling() { when(spanContextualizer.link(anyLong())).thenReturn(SpanLinkage.NONE); ConfigProperties config = mock(ConfigProperties.class); - when(config.getBoolean(CONFIG_KEY_MEMORY_ENABLED, DEFAULT_MEMORY_ENABLED)).thenReturn(true); + when(config.getBoolean(CONFIG_KEY_MEMORY_ENABLED, false)).thenReturn(true); TestAllocationEventExporter allocationEventExporter = new TestAllocationEventExporter(); From 480405a7c7a6b55788a90899eeb63a703275fe22 Mon Sep 17 00:00:00 2001 From: robsunday Date: Wed, 29 Oct 2025 18:50:00 +0100 Subject: [PATCH 02/16] Additional cleanup of Configuration class. --- ...ofilerConfigurationCustomizerProvider.java | 64 +++++++++++++++++++ .../util/ProfilerDeclarativeConfigUtil.java | 57 +++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProvider.java create mode 100644 profiler/src/main/java/com/splunk/opentelemetry/profiler/util/ProfilerDeclarativeConfigUtil.java diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProvider.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProvider.java new file mode 100644 index 000000000..e15a4da57 --- /dev/null +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProvider.java @@ -0,0 +1,64 @@ +/* + * 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 com.splunk.opentelemetry.profiler.util.ProfilerDeclarativeConfigUtil.isProfilerEnabled; + +import com.google.auto.service.AutoService; +import com.google.common.annotations.VisibleForTesting; +import io.opentelemetry.context.ContextStorage; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizer; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizerProvider; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalLanguageSpecificInstrumentationModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import java.util.Map; + +@AutoService(DeclarativeConfigurationCustomizerProvider.class) +public class ProfilerConfigurationCustomizerProvider + implements DeclarativeConfigurationCustomizerProvider { + + @Override + public int order() { + return Integer.MAX_VALUE - 1; + } + + @Override + public void customize(DeclarativeConfigurationCustomizer autoConfiguration) { + autoConfiguration.addModelCustomizer(this::customizeModel); + } + + @VisibleForTesting + OpenTelemetryConfigurationModel customizeModel(OpenTelemetryConfigurationModel model) { + if (model.getInstrumentationDevelopment() != null) { + ExperimentalLanguageSpecificInstrumentationModel javaModel = + model.getInstrumentationDevelopment().getJava(); + if (javaModel != null) { + Map javaInstrumentationProperties = javaModel.getAdditionalProperties(); + if (isProfilerEnabled(javaInstrumentationProperties)) { + if (jfrIsAvailable()) { + ContextStorage.addWrapper(JfrContextStorage::new); + } + } + } + } + return model; + } + + private boolean jfrIsAvailable() { + return JFR.instance.isAvailable(); + } +} diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/util/ProfilerDeclarativeConfigUtil.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/util/ProfilerDeclarativeConfigUtil.java new file mode 100644 index 000000000..966222302 --- /dev/null +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/util/ProfilerDeclarativeConfigUtil.java @@ -0,0 +1,57 @@ +/* + * 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.util; + +import static io.opentelemetry.sdk.autoconfigure.AdditionalPropertiesUtil.getAdditionalPropertyOrDefault; + +import com.splunk.opentelemetry.SplunkConfiguration; +import com.splunk.opentelemetry.profiler.snapshot.SnapshotProfilingConfiguration; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalLanguageSpecificInstrumentationModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import java.util.Map; + +public class ProfilerDeclarativeConfigUtil { + public static boolean isProfilerEnabled(OpenTelemetryConfigurationModel model) { + return getBoolean(model, SplunkConfiguration.PROFILER_ENABLED_PROPERTY); + } + + public static boolean isProfilerEnabled(Map javaInstrumentationProperties) { + return getBoolean(javaInstrumentationProperties, SplunkConfiguration.PROFILER_ENABLED_PROPERTY); + } + + public static boolean isSnapshotProfilingEnabled(OpenTelemetryConfigurationModel model) { + return getBoolean(model, SnapshotProfilingConfiguration.CONFIG_KEY_ENABLE_SNAPSHOT_PROFILER); + } + + private static boolean getBoolean(OpenTelemetryConfigurationModel model, String propertyName) { + if (model.getInstrumentationDevelopment() != null) { + ExperimentalLanguageSpecificInstrumentationModel java = + model.getInstrumentationDevelopment().getJava(); + + if (java != null) { + Map javaInstrumentationProperties = java.getAdditionalProperties(); + return getBoolean(javaInstrumentationProperties, propertyName); + } + } + return false; + } + + private static boolean getBoolean( + Map javaInstrumentationProperties, String propertyName) { + return getAdditionalPropertyOrDefault(javaInstrumentationProperties, propertyName, false); + } +} From 41cc08e0e5a3047f4e2dbd3b35847fba91dad740 Mon Sep 17 00:00:00 2001 From: robsunday Date: Wed, 29 Oct 2025 18:50:21 +0100 Subject: [PATCH 03/16] Additional cleanup of Configuration class. --- .../opentelemetry/profiler/Configuration.java | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java index 8ffe3ac63..eae81a32d 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java @@ -54,15 +54,9 @@ public class Configuration { private static final boolean HAS_OBJECT_ALLOCATION_SAMPLE_EVENT = getJavaVersion() >= 16; + 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 boolean DEFAULT_MEMORY_ENABLED = false; private static final Duration DEFAULT_CALL_STACK_INTERVAL = Duration.ofSeconds(10); - private static final boolean DEFAULT_INCLUDE_INTERNAL_STACKS = false; - private static final boolean DEFAULT_TRACING_STACKS_ONLY = false; - private static final int DEFAULT_STACK_DEPTH = 1024; - private static final boolean DEFAULT_MEMORY_EVENT_RATE_LIMIT_ENABLED = true; - // ObjectAllocationSample event uses 150/s in default and 300/s in profiling configuration - private static final String DEFAULT_MEMORY_EVENT_RATE = "150/s"; public static void log(ConfigProperties config) { logger.info("-----------------------"); @@ -125,16 +119,16 @@ public static String getOtlpProtocol(ConfigProperties config) { } public static boolean getMemoryEnabled(ConfigProperties config) { - return config.getBoolean(CONFIG_KEY_MEMORY_ENABLED, DEFAULT_MEMORY_ENABLED); + return config.getBoolean(CONFIG_KEY_MEMORY_ENABLED, false); } public static boolean getMemoryEventRateLimitEnabled(ConfigProperties config) { return config.getBoolean( - CONFIG_KEY_MEMORY_EVENT_RATE_LIMIT_ENABLED, DEFAULT_MEMORY_EVENT_RATE_LIMIT_ENABLED); + CONFIG_KEY_MEMORY_EVENT_RATE_LIMIT_ENABLED, true); } public static String getMemoryEventRate(ConfigProperties config) { - return config.getString(CONFIG_KEY_MEMORY_EVENT_RATE, DEFAULT_MEMORY_EVENT_RATE); + return config.getString(CONFIG_KEY_MEMORY_EVENT_RATE, "150/s"); } public static boolean getUseAllocationSampleEvent(ConfigProperties config) { @@ -149,22 +143,22 @@ public static Duration getCallStackInterval(ConfigProperties config) { public static boolean getIncludeAgentInternalStacks(ConfigProperties config) { boolean includeInternals = - config.getBoolean(CONFIG_KEY_INCLUDE_INTERNAL_STACKS, DEFAULT_INCLUDE_INTERNAL_STACKS); + config.getBoolean(CONFIG_KEY_INCLUDE_INTERNAL_STACKS, false); return config.getBoolean(CONFIG_KEY_INCLUDE_AGENT_INTERNALS, includeInternals); } public static boolean getIncludeJvmInternalStacks(ConfigProperties config) { boolean includeInternals = - config.getBoolean(CONFIG_KEY_INCLUDE_INTERNAL_STACKS, DEFAULT_INCLUDE_INTERNAL_STACKS); + config.getBoolean(CONFIG_KEY_INCLUDE_INTERNAL_STACKS, false); return config.getBoolean(CONFIG_KEY_INCLUDE_JVM_INTERNALS, includeInternals); } public static boolean getTracingStacksOnly(ConfigProperties config) { - return config.getBoolean(CONFIG_KEY_TRACING_STACKS_ONLY, DEFAULT_TRACING_STACKS_ONLY); + return config.getBoolean(CONFIG_KEY_TRACING_STACKS_ONLY, false); } public static int getStackDepth(ConfigProperties config) { - return config.getInt(CONFIG_KEY_STACK_DEPTH, DEFAULT_STACK_DEPTH); + return config.getInt(CONFIG_KEY_STACK_DEPTH, 1024); } public static boolean getKeepFiles(ConfigProperties config) { @@ -172,7 +166,7 @@ public static boolean getKeepFiles(ConfigProperties config) { } public static String getProfilerDirectory(ConfigProperties config) { - return config.getString(CONFIG_KEY_PROFILER_DIRECTORY, System.getProperty("java.io.tmpdir")); + return config.getString(CONFIG_KEY_PROFILER_DIRECTORY, DEFAULT_PROFILER_DIRECTORY); } public static Duration getRecordingDuration(ConfigProperties config) { From c56343d3dcbf2522d112c13d270ad11590010041 Mon Sep 17 00:00:00 2001 From: robsunday Date: Wed, 29 Oct 2025 18:51:31 +0100 Subject: [PATCH 04/16] Revert "Additional cleanup of Configuration class." This reverts commit 480405a7c7a6b55788a90899eeb63a703275fe22. --- ...ofilerConfigurationCustomizerProvider.java | 64 ------------------- .../util/ProfilerDeclarativeConfigUtil.java | 57 ----------------- 2 files changed, 121 deletions(-) delete mode 100644 profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProvider.java delete mode 100644 profiler/src/main/java/com/splunk/opentelemetry/profiler/util/ProfilerDeclarativeConfigUtil.java diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProvider.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProvider.java deleted file mode 100644 index e15a4da57..000000000 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProvider.java +++ /dev/null @@ -1,64 +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 com.splunk.opentelemetry.profiler.util.ProfilerDeclarativeConfigUtil.isProfilerEnabled; - -import com.google.auto.service.AutoService; -import com.google.common.annotations.VisibleForTesting; -import io.opentelemetry.context.ContextStorage; -import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizer; -import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizerProvider; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalLanguageSpecificInstrumentationModel; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; -import java.util.Map; - -@AutoService(DeclarativeConfigurationCustomizerProvider.class) -public class ProfilerConfigurationCustomizerProvider - implements DeclarativeConfigurationCustomizerProvider { - - @Override - public int order() { - return Integer.MAX_VALUE - 1; - } - - @Override - public void customize(DeclarativeConfigurationCustomizer autoConfiguration) { - autoConfiguration.addModelCustomizer(this::customizeModel); - } - - @VisibleForTesting - OpenTelemetryConfigurationModel customizeModel(OpenTelemetryConfigurationModel model) { - if (model.getInstrumentationDevelopment() != null) { - ExperimentalLanguageSpecificInstrumentationModel javaModel = - model.getInstrumentationDevelopment().getJava(); - if (javaModel != null) { - Map javaInstrumentationProperties = javaModel.getAdditionalProperties(); - if (isProfilerEnabled(javaInstrumentationProperties)) { - if (jfrIsAvailable()) { - ContextStorage.addWrapper(JfrContextStorage::new); - } - } - } - } - return model; - } - - private boolean jfrIsAvailable() { - return JFR.instance.isAvailable(); - } -} diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/util/ProfilerDeclarativeConfigUtil.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/util/ProfilerDeclarativeConfigUtil.java deleted file mode 100644 index 966222302..000000000 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/util/ProfilerDeclarativeConfigUtil.java +++ /dev/null @@ -1,57 +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.util; - -import static io.opentelemetry.sdk.autoconfigure.AdditionalPropertiesUtil.getAdditionalPropertyOrDefault; - -import com.splunk.opentelemetry.SplunkConfiguration; -import com.splunk.opentelemetry.profiler.snapshot.SnapshotProfilingConfiguration; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalLanguageSpecificInstrumentationModel; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; -import java.util.Map; - -public class ProfilerDeclarativeConfigUtil { - public static boolean isProfilerEnabled(OpenTelemetryConfigurationModel model) { - return getBoolean(model, SplunkConfiguration.PROFILER_ENABLED_PROPERTY); - } - - public static boolean isProfilerEnabled(Map javaInstrumentationProperties) { - return getBoolean(javaInstrumentationProperties, SplunkConfiguration.PROFILER_ENABLED_PROPERTY); - } - - public static boolean isSnapshotProfilingEnabled(OpenTelemetryConfigurationModel model) { - return getBoolean(model, SnapshotProfilingConfiguration.CONFIG_KEY_ENABLE_SNAPSHOT_PROFILER); - } - - private static boolean getBoolean(OpenTelemetryConfigurationModel model, String propertyName) { - if (model.getInstrumentationDevelopment() != null) { - ExperimentalLanguageSpecificInstrumentationModel java = - model.getInstrumentationDevelopment().getJava(); - - if (java != null) { - Map javaInstrumentationProperties = java.getAdditionalProperties(); - return getBoolean(javaInstrumentationProperties, propertyName); - } - } - return false; - } - - private static boolean getBoolean( - Map javaInstrumentationProperties, String propertyName) { - return getAdditionalPropertyOrDefault(javaInstrumentationProperties, propertyName, false); - } -} From 369526143a0b0fb0c8b82cb60c8e45cb482e3419 Mon Sep 17 00:00:00 2001 From: robsunday Date: Wed, 29 Oct 2025 19:27:31 +0100 Subject: [PATCH 05/16] Spotless --- .../com/splunk/opentelemetry/profiler/Configuration.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java index eae81a32d..ea98d22e3 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java @@ -123,8 +123,7 @@ public static boolean getMemoryEnabled(ConfigProperties config) { } public static boolean getMemoryEventRateLimitEnabled(ConfigProperties config) { - return config.getBoolean( - CONFIG_KEY_MEMORY_EVENT_RATE_LIMIT_ENABLED, true); + return config.getBoolean(CONFIG_KEY_MEMORY_EVENT_RATE_LIMIT_ENABLED, true); } public static String getMemoryEventRate(ConfigProperties config) { @@ -142,14 +141,12 @@ public static Duration getCallStackInterval(ConfigProperties config) { } public static boolean getIncludeAgentInternalStacks(ConfigProperties config) { - boolean includeInternals = - config.getBoolean(CONFIG_KEY_INCLUDE_INTERNAL_STACKS, false); + boolean includeInternals = config.getBoolean(CONFIG_KEY_INCLUDE_INTERNAL_STACKS, false); return config.getBoolean(CONFIG_KEY_INCLUDE_AGENT_INTERNALS, includeInternals); } public static boolean getIncludeJvmInternalStacks(ConfigProperties config) { - boolean includeInternals = - config.getBoolean(CONFIG_KEY_INCLUDE_INTERNAL_STACKS, false); + boolean includeInternals = config.getBoolean(CONFIG_KEY_INCLUDE_INTERNAL_STACKS, false); return config.getBoolean(CONFIG_KEY_INCLUDE_JVM_INTERNALS, includeInternals); } From 43bd074b5168e391724019bc31267526d0471d86 Mon Sep 17 00:00:00 2001 From: robsunday Date: Thu, 30 Oct 2025 07:44:09 +0100 Subject: [PATCH 06/16] Tests fixed --- .../opentelemetry/profiler/JfrSettingsOverridesTest.java | 4 +++- .../splunk/opentelemetry/profiler/LogExporterBuilderTest.java | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrSettingsOverridesTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrSettingsOverridesTest.java index fcb11e569..2eb1942ec 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrSettingsOverridesTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrSettingsOverridesTest.java @@ -19,6 +19,8 @@ import static com.splunk.opentelemetry.profiler.Configuration.CONFIG_KEY_CALL_STACK_INTERVAL; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -32,7 +34,7 @@ class JfrSettingsOverridesTest { @Test void testOverrides() { ConfigProperties config = mock(ConfigProperties.class); - when(config.getDuration(CONFIG_KEY_CALL_STACK_INTERVAL, Duration.ZERO)) + when(config.getDuration(eq(CONFIG_KEY_CALL_STACK_INTERVAL), any(Duration.class))) .thenReturn(Duration.ofMillis(163)); when(config.getBoolean("splunk.profiler.memory.enabled", false)).thenReturn(true); JfrSettingsOverrides overrides = new JfrSettingsOverrides(config); 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 74720bcb3..d9aadc474 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/LogExporterBuilderTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/LogExporterBuilderTest.java @@ -119,7 +119,7 @@ void testCustomEndpointGrpc() { when(config.getString(Configuration.CONFIG_KEY_PROFILER_OTLP_PROTOCOL, null)) .thenReturn("grpc"); - when(config.getString(Configuration.CONFIG_KEY_OTEL_OTLP_URL, null)) + when(config.getString(Configuration.CONFIG_KEY_OTEL_OTLP_URL)) .thenReturn("http://shadowed.example.com:9122/"); when(config.getString(Configuration.CONFIG_KEY_INGEST_URL, "http://shadowed.example.com:9122/")) .thenReturn(endpoint); @@ -142,7 +142,7 @@ void testCustomEndpointHttp() { when(config.getString(Configuration.CONFIG_KEY_PROFILER_OTLP_PROTOCOL, null)) .thenReturn("http/protobuf"); - when(config.getString(Configuration.CONFIG_KEY_OTEL_OTLP_URL, null)) + when(config.getString(Configuration.CONFIG_KEY_OTEL_OTLP_URL)) .thenReturn("http://shadowed.example.com:9122/"); when(config.getString( Configuration.CONFIG_KEY_INGEST_URL, "http://shadowed.example.com:9122/v1/logs")) From bc840e9d432e37f5d42f9b0b1735c51674d38f04 Mon Sep 17 00:00:00 2001 From: Robert Niedziela <175605712+robsunday@users.noreply.github.com> Date: Mon, 3 Nov 2025 09:00:27 +0100 Subject: [PATCH 07/16] Update profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java Co-authored-by: jason plumb <75337021+breedx-splk@users.noreply.github.com> --- .../java/com/splunk/opentelemetry/profiler/Configuration.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java index ea98d22e3..f3ea0278e 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java @@ -79,8 +79,8 @@ public static void log(ConfigProperties config) { logger.info("-----------------------"); } - private static void log(String key, Function getter) { - logger.info(String.format("%39s : %s", key, getter.apply(key))); + private static void log(String key, @Nullable Object value) { + logger.info(String.format("%39s : %s", key, value)); } public static String getConfigUrl(ConfigProperties config) { From c98a6019d38e8fcfd27b480ac76e1d7d3c5c7b65 Mon Sep 17 00:00:00 2001 From: robsunday Date: Mon, 3 Nov 2025 09:03:50 +0100 Subject: [PATCH 08/16] Code review followup --- .../opentelemetry/profiler/Configuration.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java index f3ea0278e..18b0353f2 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java @@ -23,8 +23,8 @@ import com.splunk.opentelemetry.SplunkConfiguration; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import java.time.Duration; -import java.util.function.Function; import java.util.logging.Logger; +import javax.annotation.Nullable; public class Configuration { private static final Logger logger = Logger.getLogger(Configuration.class.getName()); @@ -61,21 +61,21 @@ public class Configuration { public static void log(ConfigProperties config) { logger.info("-----------------------"); logger.info("Profiler configuration:"); - log(PROFILER_ENABLED_PROPERTY, (it) -> SplunkConfiguration.isProfilerEnabled(config)); - log(CONFIG_KEY_PROFILER_DIRECTORY, (it) -> getProfilerDirectory(config)); - log(CONFIG_KEY_RECORDING_DURATION, (it) -> getRecordingDuration(config)); - log(CONFIG_KEY_KEEP_FILES, (it) -> getKeepFiles(config)); - log(CONFIG_KEY_INGEST_URL, (it) -> getConfigUrl(config)); - log(CONFIG_KEY_OTEL_OTLP_URL, config::getString); - log(CONFIG_KEY_MEMORY_ENABLED, (it) -> getMemoryEnabled(config)); + log(PROFILER_ENABLED_PROPERTY, SplunkConfiguration.isProfilerEnabled(config)); + log(CONFIG_KEY_PROFILER_DIRECTORY, getProfilerDirectory(config)); + log(CONFIG_KEY_RECORDING_DURATION, getRecordingDuration(config)); + log(CONFIG_KEY_KEEP_FILES, getKeepFiles(config)); + log(CONFIG_KEY_INGEST_URL, getConfigUrl(config)); + log(CONFIG_KEY_OTEL_OTLP_URL, config.getString(CONFIG_KEY_OTEL_OTLP_URL)); + log(CONFIG_KEY_MEMORY_ENABLED, getMemoryEnabled(config)); if (getMemoryEventRateLimitEnabled(config)) { - log(CONFIG_KEY_MEMORY_EVENT_RATE, (it) -> getMemoryEventRate(config)); + log(CONFIG_KEY_MEMORY_EVENT_RATE, getMemoryEventRate(config)); } - log(CONFIG_KEY_CALL_STACK_INTERVAL, (it) -> getCallStackInterval(config)); - log(CONFIG_KEY_INCLUDE_AGENT_INTERNALS, (it) -> getIncludeAgentInternalStacks(config)); - log(CONFIG_KEY_INCLUDE_JVM_INTERNALS, (it) -> getIncludeJvmInternalStacks(config)); - log(CONFIG_KEY_TRACING_STACKS_ONLY, (it) -> getTracingStacksOnly(config)); - log(CONFIG_KEY_STACK_DEPTH, (it) -> getStackDepth(config)); + log(CONFIG_KEY_CALL_STACK_INTERVAL, getCallStackInterval(config)); + log(CONFIG_KEY_INCLUDE_AGENT_INTERNALS, getIncludeAgentInternalStacks(config)); + log(CONFIG_KEY_INCLUDE_JVM_INTERNALS, getIncludeJvmInternalStacks(config)); + log(CONFIG_KEY_TRACING_STACKS_ONLY, getTracingStacksOnly(config)); + log(CONFIG_KEY_STACK_DEPTH, getStackDepth(config)); logger.info("-----------------------"); } From 8dd45feaa8a10dcd06d7431ce03775f5de291c92 Mon Sep 17 00:00:00 2001 From: robsunday Date: Mon, 3 Nov 2025 09:16:55 +0100 Subject: [PATCH 09/16] Fixed issue with getConfigUrl() method Minor logging improvements --- .../opentelemetry/profiler/Configuration.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java index 18b0353f2..e2966b0ea 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java @@ -17,7 +17,6 @@ package com.splunk.opentelemetry.profiler; import static com.splunk.opentelemetry.SplunkConfiguration.PROFILER_ENABLED_PROPERTY; -import static com.splunk.opentelemetry.SplunkConfiguration.PROFILER_MEMORY_ENABLED_PROPERTY; import static java.util.logging.Level.WARNING; import com.splunk.opentelemetry.SplunkConfiguration; @@ -29,15 +28,16 @@ public class Configuration { private static final Logger logger = Logger.getLogger(Configuration.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_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_OTLP_PROTOCOL = "otel.exporter.otlp.protocol"; - static final String CONFIG_KEY_OTEL_OTLP_URL = "otel.exporter.otlp.endpoint"; - static final String CONFIG_KEY_MEMORY_ENABLED = PROFILER_MEMORY_ENABLED_PROPERTY; + 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"; @@ -63,15 +63,17 @@ public static void log(ConfigProperties config) { logger.info("Profiler configuration:"); log(PROFILER_ENABLED_PROPERTY, SplunkConfiguration.isProfilerEnabled(config)); log(CONFIG_KEY_PROFILER_DIRECTORY, getProfilerDirectory(config)); - log(CONFIG_KEY_RECORDING_DURATION, getRecordingDuration(config)); + log(CONFIG_KEY_RECORDING_DURATION, getRecordingDuration(config).toMillis() + "ms"); log(CONFIG_KEY_KEEP_FILES, getKeepFiles(config)); + log(CONFIG_KEY_PROFILER_OTLP_PROTOCOL, getOtlpProtocol(config)); log(CONFIG_KEY_INGEST_URL, getConfigUrl(config)); log(CONFIG_KEY_OTEL_OTLP_URL, config.getString(CONFIG_KEY_OTEL_OTLP_URL)); log(CONFIG_KEY_MEMORY_ENABLED, getMemoryEnabled(config)); if (getMemoryEventRateLimitEnabled(config)) { log(CONFIG_KEY_MEMORY_EVENT_RATE, getMemoryEventRate(config)); } - log(CONFIG_KEY_CALL_STACK_INTERVAL, getCallStackInterval(config)); + log(CONFIG_KEY_MEMORY_NATIVE_SAMPLING, getUseAllocationSampleEvent(config)); + log(CONFIG_KEY_CALL_STACK_INTERVAL, getCallStackInterval(config).toMillis() + "ms"); log(CONFIG_KEY_INCLUDE_AGENT_INTERNALS, getIncludeAgentInternalStacks(config)); log(CONFIG_KEY_INCLUDE_JVM_INTERNALS, getIncludeJvmInternalStacks(config)); log(CONFIG_KEY_TRACING_STACKS_ONLY, getTracingStacksOnly(config)); @@ -102,6 +104,8 @@ public static String getConfigUrl(ConfigProperties config) { } ingestUrl += "v1/logs"; } + } else { + ingestUrl = getDefaultLogsEndpoint(config); } return config.getString(CONFIG_KEY_INGEST_URL, ingestUrl); } From d1ec23b71114541dcf079d892e01f340b03647c5 Mon Sep 17 00:00:00 2001 From: robsunday Date: Mon, 3 Nov 2025 13:48:30 +0100 Subject: [PATCH 10/16] Reverting an unnecessary change --- .../java/com/splunk/opentelemetry/profiler/Configuration.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java index e2966b0ea..17a36af1f 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java @@ -104,8 +104,6 @@ public static String getConfigUrl(ConfigProperties config) { } ingestUrl += "v1/logs"; } - } else { - ingestUrl = getDefaultLogsEndpoint(config); } return config.getString(CONFIG_KEY_INGEST_URL, ingestUrl); } From 8196c59f95ab3ff99d78c8eb55fa49b932e90048 Mon Sep 17 00:00:00 2001 From: robsunday Date: Wed, 5 Nov 2025 12:49:51 +0100 Subject: [PATCH 11/16] Refactoring to let Configuration.getIngestUrl(config) return not null URL for declarative config --- .../opentelemetry/profiler/Configuration.java | 43 ++++--- .../profiler/LogExporterBuilder.java | 14 +-- .../profiler/ConfigurationTest.java | 106 +++++++++++++----- .../profiler/LogExporterBuilderTest.java | 103 ++++++++--------- 4 files changed, 161 insertions(+), 105 deletions(-) diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java index 17a36af1f..c25014cc1 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/Configuration.java @@ -66,7 +66,7 @@ public static void log(ConfigProperties config) { log(CONFIG_KEY_RECORDING_DURATION, getRecordingDuration(config).toMillis() + "ms"); log(CONFIG_KEY_KEEP_FILES, getKeepFiles(config)); log(CONFIG_KEY_PROFILER_OTLP_PROTOCOL, getOtlpProtocol(config)); - log(CONFIG_KEY_INGEST_URL, getConfigUrl(config)); + log(CONFIG_KEY_INGEST_URL, getIngestUrl(config)); log(CONFIG_KEY_OTEL_OTLP_URL, config.getString(CONFIG_KEY_OTEL_OTLP_URL)); log(CONFIG_KEY_MEMORY_ENABLED, getMemoryEnabled(config)); if (getMemoryEventRateLimitEnabled(config)) { @@ -85,27 +85,40 @@ private static void log(String key, @Nullable Object value) { logger.info(String.format("%39s : %s", key, value)); } - public static String getConfigUrl(ConfigProperties config) { - String ingestUrl = config.getString(CONFIG_KEY_OTEL_OTLP_URL); - if (ingestUrl != null) { - if (ingestUrl.startsWith("https://ingest.") - && ingestUrl.endsWith(".signalfx.com") - && config.getString(CONFIG_KEY_INGEST_URL) == null) { + public static String getIngestUrl(ConfigProperties config) { + String ingestUrl = config.getString(CONFIG_KEY_INGEST_URL); + + if (ingestUrl == null) { + String defaultIngestUrl = getDefaultLogsEndpoint(config); + ingestUrl = config.getString(CONFIG_KEY_OTEL_OTLP_URL, defaultIngestUrl); + + if (ingestUrl.startsWith("https://ingest.") && ingestUrl.endsWith(".signalfx.com")) { logger.log( WARNING, "Profiling data can not be sent to {0}, using {1} instead. " - + "You can override it by setting splunk.profiler.logs-endpoint", - new Object[] {ingestUrl, getDefaultLogsEndpoint(config)}); - return null; + + "You can override it by setting " + + CONFIG_KEY_INGEST_URL, + new Object[] {ingestUrl, defaultIngestUrl}); + return defaultIngestUrl; } + if ("http/protobuf".equals(getOtlpProtocol(config))) { - if (!ingestUrl.endsWith("/")) { - ingestUrl += "/"; - } - ingestUrl += "v1/logs"; + ingestUrl = maybeAppendHttpPath(ingestUrl); } } - return config.getString(CONFIG_KEY_INGEST_URL, 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(ConfigProperties 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 b61cb1805..7afa8096f 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/LogExporterBuilder.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/LogExporterBuilder.java @@ -47,10 +47,9 @@ static LogRecordExporter fromConfig(ConfigProperties config) { static LogRecordExporter buildGrpcExporter( ConfigProperties config, Supplier makeBuilder) { OtlpGrpcLogRecordExporterBuilder builder = makeBuilder.get(); - String ingestUrl = Configuration.getConfigUrl(config); - if (ingestUrl != null) { - builder.setEndpoint(ingestUrl); - } + String ingestUrl = Configuration.getIngestUrl(config); + builder.setEndpoint(ingestUrl); + return builder.addHeader(EXTRA_CONTENT_TYPE, STACKTRACES_HEADER_VALUE).build(); } @@ -58,7 +57,7 @@ static LogRecordExporter buildGrpcExporter( static LogRecordExporter buildHttpExporter( ConfigProperties config, Supplier makeBuilder) { OtlpHttpLogRecordExporterBuilder builder = makeBuilder.get(); - String ingestUrl = Configuration.getConfigUrl(config); + String ingestUrl = Configuration.getIngestUrl(config); OtlpConfigUtil.configureOtlpExporterBuilder( DATA_TYPE_LOGS, @@ -73,9 +72,8 @@ static LogRecordExporter buildHttpExporter( builder::setRetryPolicy, builder::setMemoryMode); - if (ingestUrl != null) { - builder.setEndpoint(ingestUrl); - } + builder.setEndpoint(ingestUrl); + return builder.addHeader(EXTRA_CONTENT_TYPE, STACKTRACES_HEADER_VALUE).build(); } } diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/ConfigurationTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/ConfigurationTest.java index b27fe4587..b4558b427 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/ConfigurationTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/ConfigurationTest.java @@ -16,8 +16,10 @@ package com.splunk.opentelemetry.profiler; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; +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; @@ -35,44 +37,89 @@ class ConfigurationTest { String logsEndpoint = "http://logs.example.com"; String otelEndpoint = "http://otel.example.com"; + String defaultLogsEndpoint = "http://localhost:4318/v1/logs"; @Test - void getConfigUrl_endpointDefined() { + void getIngestUrl_endpointDefined() { + // given ConfigProperties config = mock(ConfigProperties.class); - when(config.getString(Configuration.CONFIG_KEY_OTEL_OTLP_URL)).thenReturn(otelEndpoint); - when(config.getString(Configuration.CONFIG_KEY_INGEST_URL, otelEndpoint)) - .thenReturn(logsEndpoint); - String result = Configuration.getConfigUrl(config); - assertEquals(logsEndpoint, result); + when(config.getString(Configuration.CONFIG_KEY_INGEST_URL)).thenReturn(logsEndpoint); + + // when + String result = Configuration.getIngestUrl(config); + + // then + assertThat(result).isEqualTo(logsEndpoint); + } + + @Test + void getIngestUrl_endpointNotDefined_usedOtelGrpc() { + // given + ConfigProperties config = mock(ConfigProperties.class); + when(config.getString(Configuration.CONFIG_KEY_INGEST_URL)).thenReturn(null); + when(config.getString(eq(Configuration.CONFIG_KEY_OTEL_OTLP_URL), anyString())) + .thenReturn(otelEndpoint); + when(config.getString(eq(Configuration.CONFIG_KEY_PROFILER_OTLP_PROTOCOL), any())) + .thenReturn("grpc"); + + // when + String result = Configuration.getIngestUrl(config); + + // then + assertThat(result).isEqualTo(otelEndpoint); } @Test - void getConfigUrl_endpointNotDefined() { + void getIngestUrl_endpointNotDefined_usedOtelHttpProtobuf() { + // given ConfigProperties config = mock(ConfigProperties.class); - when(config.getString(Configuration.CONFIG_KEY_OTEL_OTLP_URL)).thenReturn(otelEndpoint); - when(config.getString(Configuration.CONFIG_KEY_INGEST_URL, otelEndpoint)) + when(config.getString(Configuration.CONFIG_KEY_INGEST_URL)).thenReturn(null); + when(config.getString(eq(Configuration.CONFIG_KEY_OTEL_OTLP_URL), anyString())) .thenReturn(otelEndpoint); - String result = Configuration.getConfigUrl(config); - assertEquals(otelEndpoint, result); + when(config.getString(eq(Configuration.CONFIG_KEY_PROFILER_OTLP_PROTOCOL), any())) + .thenReturn("http/protobuf"); + + // when + String result = Configuration.getIngestUrl(config); + + // then + assertThat(result).isEqualTo(otelEndpoint + "/v1/logs"); } @Test - void getConfigUrlNull() { + void getIngestUrl_endpointNotDefined_usedOtelHttpProtobufWithPath() { + // given + String endpoint = otelEndpoint + "/v1/logs"; + ConfigProperties config = mock(ConfigProperties.class); - when(config.getString(Configuration.CONFIG_KEY_OTEL_OTLP_URL, null)).thenReturn(null); - when(config.getString(Configuration.CONFIG_KEY_INGEST_URL, null)).thenReturn(null); - String result = Configuration.getConfigUrl(config); - assertNull(result); + when(config.getString(Configuration.CONFIG_KEY_INGEST_URL)).thenReturn(null); + when(config.getString(eq(Configuration.CONFIG_KEY_OTEL_OTLP_URL), anyString())) + .thenReturn(endpoint); + when(config.getString(eq(Configuration.CONFIG_KEY_PROFILER_OTLP_PROTOCOL), any())) + .thenReturn("http/protobuf"); + + // when + String result = Configuration.getIngestUrl(config); + + // then + assertThat(result).isEqualTo(endpoint); } @Test - void getConfigUrlSplunkRealm() { + void getIngestUrlSplunkRealm() { + // given ConfigProperties config = mock(ConfigProperties.class); - when(config.getString(Configuration.CONFIG_KEY_OTEL_OTLP_URL)) + when(config.getString(Configuration.CONFIG_KEY_INGEST_URL)).thenReturn(null); + when(config.getString(eq(Configuration.CONFIG_KEY_OTEL_OTLP_URL), anyString())) .thenReturn("https://ingest.us0.signalfx.com"); - when(config.getString(Configuration.CONFIG_KEY_INGEST_URL, null)).thenReturn(null); - String result = Configuration.getConfigUrl(config); - assertNull(result); + when(config.getString(eq(Configuration.CONFIG_KEY_PROFILER_OTLP_PROTOCOL), any())) + .thenReturn("http/protobuf"); + + // when + String result = Configuration.getIngestUrl(config); + + // then + assertThat(result).isEqualTo(defaultLogsEndpoint); } @Test @@ -80,7 +127,8 @@ void getOtlpProtocolDefault() { String result = Configuration.getOtlpProtocol( DefaultConfigProperties.create(Collections.emptyMap(), COMPONENT_LOADER)); - assertEquals("http/protobuf", result); + + assertThat(result).isEqualTo("http/protobuf"); } @Test @@ -89,16 +137,22 @@ void getOtlpProtocolOtelPropertySet() { Configuration.getOtlpProtocol( DefaultConfigProperties.create( Collections.singletonMap("otel.exporter.otlp.protocol", "test"), COMPONENT_LOADER)); - assertEquals("test", result); + + 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"); + + // when String result = Configuration.getOtlpProtocol(DefaultConfigProperties.create(map, COMPONENT_LOADER)); - assertEquals("test2", result); + + // then + assertThat(result).isEqualTo("test2"); } } 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 d9aadc474..22b20a0c9 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/LogExporterBuilderTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/LogExporterBuilderTest.java @@ -18,10 +18,10 @@ import static com.splunk.opentelemetry.profiler.LogExporterBuilder.EXTRA_CONTENT_TYPE; import static com.splunk.opentelemetry.profiler.LogExporterBuilder.STACKTRACES_HEADER_VALUE; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertSame; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -33,26 +33,51 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.logs.export.LogRecordExporter; import java.util.Map; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; class LogExporterBuilderTest { + static final String DEFAULT_HTTP_LOG_ENDPOINT = "http://localhost:4318/v1/logs"; + static final String DEFAULT_GRPC_LOG_ENDPOINT = "http://localhost:4317"; + + private MockedStatic configurationMock; + + @BeforeEach + void setUp() { + configurationMock = mockStatic(Configuration.class); + } + + @AfterEach + void tearDown() { + configurationMock.close(); + } @Test void testBuildSimpleGrpc() { + // given ConfigProperties config = mock(ConfigProperties.class); OtlpGrpcLogRecordExporterBuilder builder = mock(OtlpGrpcLogRecordExporterBuilder.class); OtlpGrpcLogRecordExporter expected = mock(OtlpGrpcLogRecordExporter.class); when(builder.addHeader(EXTRA_CONTENT_TYPE, STACKTRACES_HEADER_VALUE)).thenReturn(builder); when(builder.build()).thenReturn(expected); + configurationMock + .when(() -> Configuration.getIngestUrl(config)) + .thenReturn(DEFAULT_GRPC_LOG_ENDPOINT); + // when LogRecordExporter exporter = LogExporterBuilder.buildGrpcExporter(config, () -> builder); - assertSame(expected, exporter); - verify(builder, never()).setEndpoint(anyString()); + + // then + assertThat(exporter).isSameAs(expected); + verify(builder).setEndpoint(DEFAULT_GRPC_LOG_ENDPOINT); } @Test void testBuildSimpleHttp() { + // given ConfigProperties config = mock(ConfigProperties.class); OtlpHttpLogRecordExporterBuilder builder = mock(OtlpHttpLogRecordExporterBuilder.class); OtlpHttpLogRecordExporter expected = mock(OtlpHttpLogRecordExporter.class); @@ -60,14 +85,21 @@ void testBuildSimpleHttp() { when(builder.addHeader(EXTRA_CONTENT_TYPE, STACKTRACES_HEADER_VALUE)).thenReturn(builder); when(builder.build()).thenReturn(expected); when(config.getString("otel.exporter.otlp.protocol", "grpc")).thenReturn("http/protobuf"); + configurationMock + .when(() -> Configuration.getIngestUrl(config)) + .thenReturn(DEFAULT_HTTP_LOG_ENDPOINT); + // when LogRecordExporter exporter = LogExporterBuilder.buildHttpExporter(config, () -> builder); - assertSame(expected, exporter); - verify(builder, never()).setEndpoint(anyString()); + + // then + assertThat(exporter).isSameAs(expected); + verify(builder).setEndpoint(DEFAULT_HTTP_LOG_ENDPOINT); } @Test void extraOtlpHeaders() { + // given ConfigProperties config = mock(ConfigProperties.class); when(config.getMap("otel.exporter.otlp.headers")) .thenReturn(Map.of("foo", "bar", "bar", "baz")); @@ -80,8 +112,11 @@ void extraOtlpHeaders() { when(builder.addHeader(anyString(), anyString())).thenReturn(builder); when(builder.build()).thenReturn(expected); + // when LogRecordExporter exporter = LogExporterBuilder.buildHttpExporter(config, () -> builder); - assertSame(expected, exporter); + + // then + assertThat(exporter).isSameAs(expected); verify(builder).addHeader(EXTRA_CONTENT_TYPE, STACKTRACES_HEADER_VALUE); verify(builder).addHeader("log", "lady"); verify(builder, never()).addHeader("foo", "bar"); @@ -90,6 +125,7 @@ void extraOtlpHeaders() { @Test void extraOtlpLogSpecificHeaders() { + // given ConfigProperties config = mock(ConfigProperties.class); when(config.getMap("otel.exporter.otlp.headers")) .thenReturn(Map.of("foo", "bar", "bar", "baz")); @@ -101,56 +137,11 @@ void extraOtlpLogSpecificHeaders() { when(builder.addHeader(anyString(), anyString())).thenReturn(builder); when(builder.build()).thenReturn(expected); - LogRecordExporter exporter = LogExporterBuilder.buildHttpExporter(config, () -> builder); - assertSame(expected, exporter); - verify(builder).addHeader(EXTRA_CONTENT_TYPE, STACKTRACES_HEADER_VALUE); - } - - @Test - void testCustomEndpointGrpc() { - String endpoint = "http://example.com:9122/"; - - ConfigProperties config = mock(ConfigProperties.class); - OtlpGrpcLogRecordExporterBuilder builder = mock(OtlpGrpcLogRecordExporterBuilder.class); - OtlpGrpcLogRecordExporter expected = mock(OtlpGrpcLogRecordExporter.class); - - when(builder.addHeader(EXTRA_CONTENT_TYPE, STACKTRACES_HEADER_VALUE)).thenReturn(builder); - when(builder.build()).thenReturn(expected); - - when(config.getString(Configuration.CONFIG_KEY_PROFILER_OTLP_PROTOCOL, null)) - .thenReturn("grpc"); - when(config.getString(Configuration.CONFIG_KEY_OTEL_OTLP_URL)) - .thenReturn("http://shadowed.example.com:9122/"); - when(config.getString(Configuration.CONFIG_KEY_INGEST_URL, "http://shadowed.example.com:9122/")) - .thenReturn(endpoint); - LogRecordExporter exporter = LogExporterBuilder.buildGrpcExporter(config, () -> builder); - - assertNotNull(exporter); - verify(builder).setEndpoint(endpoint); - } - - @Test - void testCustomEndpointHttp() { - String endpoint = "http://example.com:9122/"; - - ConfigProperties config = mock(ConfigProperties.class); - OtlpHttpLogRecordExporterBuilder builder = mock(OtlpHttpLogRecordExporterBuilder.class); - OtlpHttpLogRecordExporter expected = mock(OtlpHttpLogRecordExporter.class); - - when(builder.addHeader(EXTRA_CONTENT_TYPE, STACKTRACES_HEADER_VALUE)).thenReturn(builder); - when(builder.build()).thenReturn(expected); - - when(config.getString(Configuration.CONFIG_KEY_PROFILER_OTLP_PROTOCOL, null)) - .thenReturn("http/protobuf"); - when(config.getString(Configuration.CONFIG_KEY_OTEL_OTLP_URL)) - .thenReturn("http://shadowed.example.com:9122/"); - when(config.getString( - Configuration.CONFIG_KEY_INGEST_URL, "http://shadowed.example.com:9122/v1/logs")) - .thenReturn(endpoint); - when(config.getString("otel.exporter.otlp.protocol", "grpc")).thenReturn("http/protobuf"); + // when LogRecordExporter exporter = LogExporterBuilder.buildHttpExporter(config, () -> builder); - assertNotNull(exporter); - verify(builder).setEndpoint(endpoint); + // then + assertThat(exporter).isSameAs(expected); + verify(builder).addHeader(EXTRA_CONTENT_TYPE, STACKTRACES_HEADER_VALUE); } } From 5fdc2dc542d7cce077352d131c9a475a27ada125 Mon Sep 17 00:00:00 2001 From: robsunday Date: Fri, 7 Nov 2025 12:24:58 +0100 Subject: [PATCH 12/16] Implement profiler configuration customizer to support JFR --- profiler/build.gradle.kts | 1 + .../splunk/opentelemetry/profiler/JFR.java | 6 +- .../opentelemetry/profiler/JfrActivator.java | 6 +- .../opentelemetry/profiler/JfrRecorder.java | 2 +- ...ofilerConfigurationCustomizerProvider.java | 51 ++++++++++++++++ .../opentelemetry/profiler/SdkCustomizer.java | 2 +- .../util/ProfilerDeclarativeConfigUtil.java | 48 +++++++++++++++ ...erConfigurationCustomizerProviderTest.java | 60 +++++++++++++++++++ 8 files changed, 170 insertions(+), 6 deletions(-) create mode 100644 profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProvider.java create mode 100644 profiler/src/main/java/com/splunk/opentelemetry/profiler/util/ProfilerDeclarativeConfigUtil.java create mode 100644 profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProviderTest.java diff --git a/profiler/build.gradle.kts b/profiler/build.gradle.kts index 0b8d83682..113fea5e2 100644 --- a/profiler/build.gradle.kts +++ b/profiler/build.gradle.kts @@ -41,6 +41,7 @@ dependencies { compileOnly("com.google.auto.service:auto-service") testImplementation(project(":custom")) + testImplementation(project(":testing:common")) testImplementation("io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api") testImplementation("io.opentelemetry.javaagent:opentelemetry-testing-common") testImplementation("io.grpc:grpc-netty") diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/JFR.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/JFR.java index 2863bcba7..3e8d6b5d9 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/JFR.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/JFR.java @@ -36,9 +36,13 @@ class JFR { private static final Logger logger = Logger.getLogger(JFR.class.getName()); - public static final JFR instance = new JFR(); + private static final JFR instance = new JFR(); private static final boolean jfrAvailable = checkJfr(); + public static JFR getInstance() { + return instance; + } + private static boolean checkJfr() { try { JFR.class.getClassLoader().loadClass("jdk.jfr.FlightRecorder"); diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java index 41b3bc9ba..135637a39 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java @@ -72,7 +72,7 @@ private boolean notClearForTakeoff(ConfigProperties config) { logger.fine("Profiler is not enabled."); return true; } - if (!JFR.instance.isAvailable()) { + if (!JFR.getInstance().isAvailable()) { logger.warning( "JDK Flight Recorder (JFR) is not available in this JVM. Profiling is disabled."); return true; @@ -117,7 +117,7 @@ private void activateJfrAndRunForever(ConfigProperties config, Resource resource RecordingFileNamingConvention namingConvention = new RecordingFileNamingConvention(outputDir); int stackDepth = Configuration.getStackDepth(config); - JFR.instance.setStackDepth(stackDepth); + JFR.getInstance().setStackDepth(stackDepth); // can't be null, default value is set in Configuration.getProperties Duration recordingDuration = Configuration.getRecordingDuration(config); @@ -165,7 +165,7 @@ private void activateJfrAndRunForever(ConfigProperties config, Resource resource JfrRecorder.builder() .settings(jfrSettings) .maxAgeDuration(recordingDuration.multipliedBy(10)) - .jfr(JFR.instance) + .jfr(JFR.getInstance()) .onNewRecording(jfrRecordingHandler) .namingConvention(namingConvention) .keepRecordingFiles(keepFiles) diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrRecorder.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrRecorder.java index 24ff15538..15251c667 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrRecorder.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrRecorder.java @@ -132,7 +132,7 @@ public static class Builder { private RecordingFileNamingConvention namingConvention; private Map settings; private Duration maxAgeDuration; - private JFR jfr = JFR.instance; + private JFR jfr = JFR.getInstance(); private Consumer onNewRecording; private boolean keepRecordingFiles; diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProvider.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProvider.java new file mode 100644 index 000000000..f33291d86 --- /dev/null +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProvider.java @@ -0,0 +1,51 @@ +/* + * 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 com.splunk.opentelemetry.profiler.util.ProfilerDeclarativeConfigUtil.isProfilerEnabled; + +import com.google.auto.service.AutoService; +import com.google.common.annotations.VisibleForTesting; +import io.opentelemetry.context.ContextStorage; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizer; +import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizerProvider; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; + +@AutoService(DeclarativeConfigurationCustomizerProvider.class) +public class ProfilerConfigurationCustomizerProvider + implements DeclarativeConfigurationCustomizerProvider { + + @Override + public void customize(DeclarativeConfigurationCustomizer autoConfiguration) { + autoConfiguration.addModelCustomizer(this::customizeModel); + } + + @VisibleForTesting + OpenTelemetryConfigurationModel customizeModel(OpenTelemetryConfigurationModel model) { + if (isProfilerEnabled(model)) { + if (jfrIsAvailable()) { + ContextStorage.addWrapper(JfrContextStorage::new); + } + } + + return model; + } + + private boolean jfrIsAvailable() { + return JFR.getInstance().isAvailable(); + } +} diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/SdkCustomizer.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/SdkCustomizer.java index 21c215af0..bf98a6abc 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/SdkCustomizer.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/SdkCustomizer.java @@ -40,7 +40,7 @@ public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) { } private boolean jfrIsAvailable() { - return JFR.instance.isAvailable(); + return JFR.getInstance().isAvailable(); } private boolean jfrIsEnabledInConfig(ConfigProperties config) { diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/util/ProfilerDeclarativeConfigUtil.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/util/ProfilerDeclarativeConfigUtil.java new file mode 100644 index 000000000..12cdcff7a --- /dev/null +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/util/ProfilerDeclarativeConfigUtil.java @@ -0,0 +1,48 @@ +/* + * 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.util; + +import static io.opentelemetry.sdk.autoconfigure.AdditionalPropertiesUtil.getAdditionalPropertyOrDefault; + +import com.splunk.opentelemetry.SplunkConfiguration; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalLanguageSpecificInstrumentationModel; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import java.util.Map; + +public class ProfilerDeclarativeConfigUtil { + public static boolean isProfilerEnabled(OpenTelemetryConfigurationModel model) { + return getBoolean(model, SplunkConfiguration.PROFILER_ENABLED_PROPERTY); + } + + private static boolean getBoolean(OpenTelemetryConfigurationModel model, String propertyName) { + if (model.getInstrumentationDevelopment() != null) { + ExperimentalLanguageSpecificInstrumentationModel java = + model.getInstrumentationDevelopment().getJava(); + + if (java != null) { + Map javaInstrumentationProperties = java.getAdditionalProperties(); + return getBoolean(javaInstrumentationProperties, propertyName); + } + } + return false; + } + + private static boolean getBoolean( + Map javaInstrumentationProperties, String propertyName) { + return getAdditionalPropertyOrDefault(javaInstrumentationProperties, propertyName, false); + } +} diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProviderTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProviderTest.java new file mode 100644 index 000000000..32e1ab844 --- /dev/null +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProviderTest.java @@ -0,0 +1,60 @@ +/* + * 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 com.splunk.opentelemetry.testing.declarativeconfig.DeclarativeConfigTestUtil.parseAndCustomizeModel; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +import io.opentelemetry.context.ContextStorage; +import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +class ProfilerConfigurationCustomizerProviderTest { + @Test + void testCustomizer() { + try (MockedStatic jfrMock = mockStatic(JFR.class); + MockedStatic contextStorageMock = mockStatic(ContextStorage.class)) { + // given + var yaml = + """ + file_format: "1.0-rc.1" + instrumentation/development: + java: + splunk: + profiler: + enabled: true + """; + var jfrInstanceMock = mock(JFR.class); + when(jfrInstanceMock.isAvailable()).thenReturn(true); + jfrMock.when(JFR::getInstance).thenReturn(jfrInstanceMock); + + var testedCustomizer = new ProfilerConfigurationCustomizerProvider(); + + // when + OpenTelemetryConfigurationModel model = parseAndCustomizeModel(yaml, testedCustomizer); + + // then + assertThat(model).isNotNull(); + contextStorageMock.verify(() -> ContextStorage.addWrapper(any())); + } + } +} From 1be7e49180bef39c80700757b28fe72369f5a2ce Mon Sep 17 00:00:00 2001 From: robsunday Date: Thu, 13 Nov 2025 15:51:29 +0100 Subject: [PATCH 13/16] Code review followup - refactoring JFR support --- .../opentelemetry/profiler/JfrActivator.java | 19 ++- ...ofilerConfigurationCustomizerProvider.java | 51 -------- .../opentelemetry/profiler/SdkCustomizer.java | 49 -------- .../profiler/JfrActivatorTest.java | 114 ++++++++++++++++++ ...erConfigurationCustomizerProviderTest.java | 60 --------- 5 files changed, 132 insertions(+), 161 deletions(-) delete mode 100644 profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProvider.java delete mode 100644 profiler/src/main/java/com/splunk/opentelemetry/profiler/SdkCustomizer.java create mode 100644 profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrActivatorTest.java delete mode 100644 profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProviderTest.java diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java index 135637a39..61c857e94 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java @@ -22,6 +22,7 @@ import static java.util.logging.Level.WARNING; import com.google.auto.service.AutoService; +import com.google.common.annotations.VisibleForTesting; import com.splunk.opentelemetry.SplunkConfiguration; import com.splunk.opentelemetry.profiler.allocation.exporter.AllocationEventExporter; import com.splunk.opentelemetry.profiler.allocation.exporter.PprofAllocationEventExporter; @@ -30,6 +31,7 @@ import com.splunk.opentelemetry.profiler.exporter.PprofCpuEventExporter; import com.splunk.opentelemetry.profiler.util.HelpfulExecutors; import io.opentelemetry.api.logs.Logger; +import io.opentelemetry.context.ContextStorage; import io.opentelemetry.javaagent.extension.AgentListener; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; @@ -51,7 +53,16 @@ public class JfrActivator implements AgentListener { private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(JfrActivator.class.getName()); - private final ExecutorService executor = HelpfulExecutors.newSingleThreadExecutor("JFR Profiler"); + private final ExecutorService executor; + + public JfrActivator() { + this(HelpfulExecutors.newSingleThreadExecutor("JFR Profiler")); + } + + @VisibleForTesting + JfrActivator(ExecutorService executor) { + this.executor = executor; + } @Override public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) { @@ -62,6 +73,8 @@ public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetr Configuration.log(config); logger.info("Profiler is active."); + setupContextStorage(); + executor.submit( logUncaught( () -> activateJfrAndRunForever(config, getResource(autoConfiguredOpenTelemetrySdk)))); @@ -218,4 +231,8 @@ private Map buildJfrSettings(ConfigProperties config) { JfrSettingsOverrides overrides = new JfrSettingsOverrides(config); return overrides.apply(jfrSettings); } + + private static void setupContextStorage() { + ContextStorage.addWrapper(JfrContextStorage::new); + } } diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProvider.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProvider.java deleted file mode 100644 index f33291d86..000000000 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProvider.java +++ /dev/null @@ -1,51 +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 com.splunk.opentelemetry.profiler.util.ProfilerDeclarativeConfigUtil.isProfilerEnabled; - -import com.google.auto.service.AutoService; -import com.google.common.annotations.VisibleForTesting; -import io.opentelemetry.context.ContextStorage; -import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizer; -import io.opentelemetry.sdk.extension.incubator.fileconfig.DeclarativeConfigurationCustomizerProvider; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; - -@AutoService(DeclarativeConfigurationCustomizerProvider.class) -public class ProfilerConfigurationCustomizerProvider - implements DeclarativeConfigurationCustomizerProvider { - - @Override - public void customize(DeclarativeConfigurationCustomizer autoConfiguration) { - autoConfiguration.addModelCustomizer(this::customizeModel); - } - - @VisibleForTesting - OpenTelemetryConfigurationModel customizeModel(OpenTelemetryConfigurationModel model) { - if (isProfilerEnabled(model)) { - if (jfrIsAvailable()) { - ContextStorage.addWrapper(JfrContextStorage::new); - } - } - - return model; - } - - private boolean jfrIsAvailable() { - return JFR.getInstance().isAvailable(); - } -} diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/SdkCustomizer.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/SdkCustomizer.java deleted file mode 100644 index bf98a6abc..000000000 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/SdkCustomizer.java +++ /dev/null @@ -1,49 +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.Collections.emptyMap; - -import com.google.auto.service.AutoService; -import com.splunk.opentelemetry.SplunkConfiguration; -import io.opentelemetry.context.ContextStorage; -import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; -import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; -import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; - -@AutoService(AutoConfigurationCustomizerProvider.class) -public class SdkCustomizer implements AutoConfigurationCustomizerProvider { - - @Override - public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) { - autoConfigurationCustomizer.addPropertiesCustomizer( - config -> { - if (jfrIsAvailable() && jfrIsEnabledInConfig(config)) { - ContextStorage.addWrapper(JfrContextStorage::new); - } - return emptyMap(); - }); - } - - private boolean jfrIsAvailable() { - return JFR.getInstance().isAvailable(); - } - - private boolean jfrIsEnabledInConfig(ConfigProperties config) { - return SplunkConfiguration.isProfilerEnabled(config); - } -} diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrActivatorTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrActivatorTest.java new file mode 100644 index 000000000..847df7ec7 --- /dev/null +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrActivatorTest.java @@ -0,0 +1,114 @@ +package com.splunk.opentelemetry.profiler; + +import static com.splunk.opentelemetry.testing.declarativeconfig.DeclarativeConfigTestUtil.createAutoConfiguredSdk; +import static com.splunk.opentelemetry.testing.declarativeconfig.DeclarativeConfigTestUtil.toYamlString; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import io.opentelemetry.context.ContextStorage; +import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import java.io.IOException; +import java.nio.file.Path; +import java.util.concurrent.ExecutorService; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.MockedStatic; + +class JfrActivatorTest { + @Test + void shouldActivateJfrRecording(@TempDir Path tempDir) throws IOException { + try (MockedStatic jfrMock = mockStatic(JFR.class); + MockedStatic contextStorageMock = mockStatic(ContextStorage.class)) { + + // given + String yaml = toYamlString( + "file_format: \"1.0-rc.1\"", + "instrumentation/development:", + " java:", + " splunk:", + " profiler:", + " enabled: true" + ); + AutoConfiguredOpenTelemetrySdk sdk = createAutoConfiguredSdk(yaml, tempDir); + + var jfrInstanceMock = mock(JFR.class); + when(jfrInstanceMock.isAvailable()).thenReturn(true); + jfrMock.when(JFR::getInstance).thenReturn(jfrInstanceMock); + + ExecutorService executorMock = mock(ExecutorService.class); + JfrActivator activator = new JfrActivator(executorMock); + + // when + activator.afterAgent(sdk); + + // then + contextStorageMock.verify(() -> ContextStorage.addWrapper(any())); + verify(executorMock).submit(any(Runnable.class)); + } + } + + @Test + void shouldNotActivateJfrRecording_JfrNotAvailable(@TempDir Path tempDir) throws IOException { + try (MockedStatic jfrMock = mockStatic(JFR.class); + MockedStatic contextStorageMock = mockStatic(ContextStorage.class)) { + + // given + String yaml = toYamlString( + "file_format: \"1.0-rc.1\"", + "instrumentation/development:", + " java:", + " splunk:", + " profiler:", + " enabled: true" + ); + AutoConfiguredOpenTelemetrySdk sdk = createAutoConfiguredSdk(yaml, tempDir); + + var jfrInstanceMock = mock(JFR.class); + when(jfrInstanceMock.isAvailable()).thenReturn(false); + jfrMock.when(JFR::getInstance).thenReturn(jfrInstanceMock); + + ExecutorService executorMock = mock(ExecutorService.class); + JfrActivator activator = new JfrActivator(executorMock); + + // when + activator.afterAgent(sdk); + + // then + contextStorageMock.verifyNoInteractions(); + verifyNoInteractions(executorMock); + } + } + + @Test + void shouldNotActivateJfrRecording_profilerDisabled(@TempDir Path tempDir) throws IOException { + try (MockedStatic jfrMock = mockStatic(JFR.class); + MockedStatic contextStorageMock = mockStatic(ContextStorage.class)) { + + // given + String yaml = toYamlString( + "file_format: \"1.0-rc.1\"", + "instrumentation/development:", + " java:" + ); + AutoConfiguredOpenTelemetrySdk sdk = createAutoConfiguredSdk(yaml, tempDir); + + var jfrInstanceMock = mock(JFR.class); + when(jfrInstanceMock.isAvailable()).thenReturn(true); + jfrMock.when(JFR::getInstance).thenReturn(jfrInstanceMock); + + ExecutorService executorMock = mock(ExecutorService.class); + JfrActivator activator = new JfrActivator(executorMock); + + // when + activator.afterAgent(sdk); + + // then + contextStorageMock.verifyNoInteractions(); + verifyNoInteractions(executorMock); + } + } +} diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProviderTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProviderTest.java deleted file mode 100644 index 32e1ab844..000000000 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/ProfilerConfigurationCustomizerProviderTest.java +++ /dev/null @@ -1,60 +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 com.splunk.opentelemetry.testing.declarativeconfig.DeclarativeConfigTestUtil.parseAndCustomizeModel; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.when; - -import io.opentelemetry.context.ContextStorage; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; -import org.junit.jupiter.api.Test; -import org.mockito.MockedStatic; - -class ProfilerConfigurationCustomizerProviderTest { - @Test - void testCustomizer() { - try (MockedStatic jfrMock = mockStatic(JFR.class); - MockedStatic contextStorageMock = mockStatic(ContextStorage.class)) { - // given - var yaml = - """ - file_format: "1.0-rc.1" - instrumentation/development: - java: - splunk: - profiler: - enabled: true - """; - var jfrInstanceMock = mock(JFR.class); - when(jfrInstanceMock.isAvailable()).thenReturn(true); - jfrMock.when(JFR::getInstance).thenReturn(jfrInstanceMock); - - var testedCustomizer = new ProfilerConfigurationCustomizerProvider(); - - // when - OpenTelemetryConfigurationModel model = parseAndCustomizeModel(yaml, testedCustomizer); - - // then - assertThat(model).isNotNull(); - contextStorageMock.verify(() -> ContextStorage.addWrapper(any())); - } - } -} From 1603c2ef8825cb3d4fc6037a824e7bfdb84c48ec Mon Sep 17 00:00:00 2001 From: robsunday Date: Thu, 13 Nov 2025 16:18:35 +0100 Subject: [PATCH 14/16] Code review followup - refactoring JFR support --- .../util/ProfilerDeclarativeConfigUtil.java | 48 ------------------- 1 file changed, 48 deletions(-) delete mode 100644 profiler/src/main/java/com/splunk/opentelemetry/profiler/util/ProfilerDeclarativeConfigUtil.java diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/util/ProfilerDeclarativeConfigUtil.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/util/ProfilerDeclarativeConfigUtil.java deleted file mode 100644 index 12cdcff7a..000000000 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/util/ProfilerDeclarativeConfigUtil.java +++ /dev/null @@ -1,48 +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.util; - -import static io.opentelemetry.sdk.autoconfigure.AdditionalPropertiesUtil.getAdditionalPropertyOrDefault; - -import com.splunk.opentelemetry.SplunkConfiguration; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalLanguageSpecificInstrumentationModel; -import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel; -import java.util.Map; - -public class ProfilerDeclarativeConfigUtil { - public static boolean isProfilerEnabled(OpenTelemetryConfigurationModel model) { - return getBoolean(model, SplunkConfiguration.PROFILER_ENABLED_PROPERTY); - } - - private static boolean getBoolean(OpenTelemetryConfigurationModel model, String propertyName) { - if (model.getInstrumentationDevelopment() != null) { - ExperimentalLanguageSpecificInstrumentationModel java = - model.getInstrumentationDevelopment().getJava(); - - if (java != null) { - Map javaInstrumentationProperties = java.getAdditionalProperties(); - return getBoolean(javaInstrumentationProperties, propertyName); - } - } - return false; - } - - private static boolean getBoolean( - Map javaInstrumentationProperties, String propertyName) { - return getAdditionalPropertyOrDefault(javaInstrumentationProperties, propertyName, false); - } -} From 851781dde2ccba4fb7e590c944c8ea419c7cba10 Mon Sep 17 00:00:00 2001 From: robsunday Date: Thu, 13 Nov 2025 16:31:02 +0100 Subject: [PATCH 15/16] Code review followup - refactoring JFR support --- .../opentelemetry/profiler/JfrActivator.java | 15 ++++++---- .../profiler/JfrActivatorTest.java | 30 ++++++++----------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java index 61c857e94..01f2fe3ae 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java @@ -54,13 +54,18 @@ public class JfrActivator implements AgentListener { private static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(JfrActivator.class.getName()); private final ExecutorService executor; + private final JFR jfr; public JfrActivator() { - this(HelpfulExecutors.newSingleThreadExecutor("JFR Profiler")); + this( + JFR.getInstance(), + HelpfulExecutors.newSingleThreadExecutor("JFR Profiler") + ); } @VisibleForTesting - JfrActivator(ExecutorService executor) { + JfrActivator(JFR jfr, ExecutorService executor) { + this.jfr = jfr; this.executor = executor; } @@ -85,7 +90,7 @@ private boolean notClearForTakeoff(ConfigProperties config) { logger.fine("Profiler is not enabled."); return true; } - if (!JFR.getInstance().isAvailable()) { + if (!jfr.isAvailable()) { logger.warning( "JDK Flight Recorder (JFR) is not available in this JVM. Profiling is disabled."); return true; @@ -130,7 +135,7 @@ private void activateJfrAndRunForever(ConfigProperties config, Resource resource RecordingFileNamingConvention namingConvention = new RecordingFileNamingConvention(outputDir); int stackDepth = Configuration.getStackDepth(config); - JFR.getInstance().setStackDepth(stackDepth); + jfr.setStackDepth(stackDepth); // can't be null, default value is set in Configuration.getProperties Duration recordingDuration = Configuration.getRecordingDuration(config); @@ -178,7 +183,7 @@ private void activateJfrAndRunForever(ConfigProperties config, Resource resource JfrRecorder.builder() .settings(jfrSettings) .maxAgeDuration(recordingDuration.multipliedBy(10)) - .jfr(JFR.getInstance()) + .jfr(jfr) .onNewRecording(jfrRecordingHandler) .namingConvention(namingConvention) .keepRecordingFiles(keepFiles) diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrActivatorTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrActivatorTest.java index 847df7ec7..ae5053ab2 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrActivatorTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrActivatorTest.java @@ -21,8 +21,7 @@ class JfrActivatorTest { @Test void shouldActivateJfrRecording(@TempDir Path tempDir) throws IOException { - try (MockedStatic jfrMock = mockStatic(JFR.class); - MockedStatic contextStorageMock = mockStatic(ContextStorage.class)) { + try (MockedStatic contextStorageMock = mockStatic(ContextStorage.class)) { // given String yaml = toYamlString( @@ -35,12 +34,11 @@ void shouldActivateJfrRecording(@TempDir Path tempDir) throws IOException { ); AutoConfiguredOpenTelemetrySdk sdk = createAutoConfiguredSdk(yaml, tempDir); - var jfrInstanceMock = mock(JFR.class); - when(jfrInstanceMock.isAvailable()).thenReturn(true); - jfrMock.when(JFR::getInstance).thenReturn(jfrInstanceMock); + var jfrMock = mock(JFR.class); + when(jfrMock.isAvailable()).thenReturn(true); ExecutorService executorMock = mock(ExecutorService.class); - JfrActivator activator = new JfrActivator(executorMock); + JfrActivator activator = new JfrActivator(jfrMock, executorMock); // when activator.afterAgent(sdk); @@ -53,8 +51,7 @@ void shouldActivateJfrRecording(@TempDir Path tempDir) throws IOException { @Test void shouldNotActivateJfrRecording_JfrNotAvailable(@TempDir Path tempDir) throws IOException { - try (MockedStatic jfrMock = mockStatic(JFR.class); - MockedStatic contextStorageMock = mockStatic(ContextStorage.class)) { + try (MockedStatic contextStorageMock = mockStatic(ContextStorage.class)) { // given String yaml = toYamlString( @@ -67,12 +64,11 @@ void shouldNotActivateJfrRecording_JfrNotAvailable(@TempDir Path tempDir) throws ); AutoConfiguredOpenTelemetrySdk sdk = createAutoConfiguredSdk(yaml, tempDir); - var jfrInstanceMock = mock(JFR.class); - when(jfrInstanceMock.isAvailable()).thenReturn(false); - jfrMock.when(JFR::getInstance).thenReturn(jfrInstanceMock); + var jfrMock = mock(JFR.class); + when(jfrMock.isAvailable()).thenReturn(false); ExecutorService executorMock = mock(ExecutorService.class); - JfrActivator activator = new JfrActivator(executorMock); + JfrActivator activator = new JfrActivator(jfrMock, executorMock); // when activator.afterAgent(sdk); @@ -85,8 +81,7 @@ void shouldNotActivateJfrRecording_JfrNotAvailable(@TempDir Path tempDir) throws @Test void shouldNotActivateJfrRecording_profilerDisabled(@TempDir Path tempDir) throws IOException { - try (MockedStatic jfrMock = mockStatic(JFR.class); - MockedStatic contextStorageMock = mockStatic(ContextStorage.class)) { + try (MockedStatic contextStorageMock = mockStatic(ContextStorage.class)) { // given String yaml = toYamlString( @@ -96,12 +91,11 @@ void shouldNotActivateJfrRecording_profilerDisabled(@TempDir Path tempDir) throw ); AutoConfiguredOpenTelemetrySdk sdk = createAutoConfiguredSdk(yaml, tempDir); - var jfrInstanceMock = mock(JFR.class); - when(jfrInstanceMock.isAvailable()).thenReturn(true); - jfrMock.when(JFR::getInstance).thenReturn(jfrInstanceMock); + var jfrMock = mock(JFR.class); + when(jfrMock.isAvailable()).thenReturn(true); ExecutorService executorMock = mock(ExecutorService.class); - JfrActivator activator = new JfrActivator(executorMock); + JfrActivator activator = new JfrActivator(jfrMock, executorMock); // when activator.afterAgent(sdk); From 52c4101556a52bad8c55d8eff1ef2838d7176cdf Mon Sep 17 00:00:00 2001 From: robsunday Date: Thu, 13 Nov 2025 19:07:50 +0100 Subject: [PATCH 16/16] Spotless --- .../opentelemetry/profiler/JfrActivator.java | 5 +- .../profiler/JfrActivatorTest.java | 55 ++++++++++++------- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java b/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java index 01f2fe3ae..209b5e3d4 100644 --- a/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java +++ b/profiler/src/main/java/com/splunk/opentelemetry/profiler/JfrActivator.java @@ -57,10 +57,7 @@ public class JfrActivator implements AgentListener { private final JFR jfr; public JfrActivator() { - this( - JFR.getInstance(), - HelpfulExecutors.newSingleThreadExecutor("JFR Profiler") - ); + this(JFR.getInstance(), HelpfulExecutors.newSingleThreadExecutor("JFR Profiler")); } @VisibleForTesting diff --git a/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrActivatorTest.java b/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrActivatorTest.java index ae5053ab2..c66a8413f 100644 --- a/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrActivatorTest.java +++ b/profiler/src/test/java/com/splunk/opentelemetry/profiler/JfrActivatorTest.java @@ -1,3 +1,19 @@ +/* + * 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 com.splunk.opentelemetry.testing.declarativeconfig.DeclarativeConfigTestUtil.createAutoConfiguredSdk; @@ -24,14 +40,14 @@ void shouldActivateJfrRecording(@TempDir Path tempDir) throws IOException { try (MockedStatic contextStorageMock = mockStatic(ContextStorage.class)) { // given - String yaml = toYamlString( - "file_format: \"1.0-rc.1\"", - "instrumentation/development:", - " java:", - " splunk:", - " profiler:", - " enabled: true" - ); + String yaml = + toYamlString( + "file_format: \"1.0-rc.1\"", + "instrumentation/development:", + " java:", + " splunk:", + " profiler:", + " enabled: true"); AutoConfiguredOpenTelemetrySdk sdk = createAutoConfiguredSdk(yaml, tempDir); var jfrMock = mock(JFR.class); @@ -54,14 +70,14 @@ void shouldNotActivateJfrRecording_JfrNotAvailable(@TempDir Path tempDir) throws try (MockedStatic contextStorageMock = mockStatic(ContextStorage.class)) { // given - String yaml = toYamlString( - "file_format: \"1.0-rc.1\"", - "instrumentation/development:", - " java:", - " splunk:", - " profiler:", - " enabled: true" - ); + String yaml = + toYamlString( + "file_format: \"1.0-rc.1\"", + "instrumentation/development:", + " java:", + " splunk:", + " profiler:", + " enabled: true"); AutoConfiguredOpenTelemetrySdk sdk = createAutoConfiguredSdk(yaml, tempDir); var jfrMock = mock(JFR.class); @@ -84,11 +100,8 @@ void shouldNotActivateJfrRecording_profilerDisabled(@TempDir Path tempDir) throw try (MockedStatic contextStorageMock = mockStatic(ContextStorage.class)) { // given - String yaml = toYamlString( - "file_format: \"1.0-rc.1\"", - "instrumentation/development:", - " java:" - ); + String yaml = + toYamlString("file_format: \"1.0-rc.1\"", "instrumentation/development:", " java:"); AutoConfiguredOpenTelemetrySdk sdk = createAutoConfiguredSdk(yaml, tempDir); var jfrMock = mock(JFR.class);