Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c592854
work in progress
breedx-splk Jun 8, 2026
d80e8b9
simplify by making a test config class impl
breedx-splk Jun 8, 2026
ff88ec8
factor out the creational stuff into a builder, add tests
breedx-splk Jun 8, 2026
daa85ff
default
breedx-splk Jun 8, 2026
0706870
rework tests
breedx-splk Jun 8, 2026
dfc76ed
rename
breedx-splk Jun 8, 2026
7f880ff
import logger
breedx-splk Jun 10, 2026
37e5776
work on tests
breedx-splk Jun 11, 2026
16eb882
rename sequencer -> flusher.
breedx-splk Jun 11, 2026
2bec48f
spotless
breedx-splk Jun 11, 2026
6976881
Merge branch 'main' into opamp-tmp
robsunday Jun 12, 2026
3e8642b
POC
robsunday Jun 12, 2026
6b553f9
POC code refactored into production
robsunday Jun 16, 2026
da2bd25
Test fixed
robsunday Jun 17, 2026
67ad654
Added possibility to turn off profiler from remote config
robsunday Jun 17, 2026
7676238
fix
robsunday Jun 17, 2026
7bf5bc4
code review followup
robsunday Jun 18, 2026
4d25025
Merge branch 'main' into opamp-tmp
robsunday Jun 19, 2026
dd1c3b1
Additional JFR check added
robsunday Jun 19, 2026
d345784
Enable reporting effective config after change
robsunday Jun 19, 2026
14ae567
Enable reporting effective config after change
robsunday Jun 19, 2026
8f66b55
Spotless
robsunday Jun 19, 2026
476da9c
Merge branch 'main' into opamp-remote-effective-config-feedback
robsunday Jun 22, 2026
944e0b8
Merge fix
robsunday Jun 22, 2026
0e10073
Remote config status reporting improved
robsunday Jun 23, 2026
06c7fe9
Refactoring of profiler config
robsunday Jun 23, 2026
3c46ecd
Use configuration supplier in few additional places
robsunday Jun 23, 2026
dfda42b
cleanup
robsunday Jun 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@
import com.splunk.opentelemetry.instrumentation.jvmmetrics.otel.OtelAllocatedMemoryMetrics;
import com.splunk.opentelemetry.instrumentation.jvmmetrics.otel.OtelGcMemoryMetrics;
import com.splunk.opentelemetry.profiler.ProfilerConfiguration;
import com.splunk.opentelemetry.profiler.ProfilerDeclarativeConfiguration;
import com.splunk.opentelemetry.profiler.ProfilerEnvVarsConfiguration;
import io.opentelemetry.javaagent.extension.AgentListener;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
Expand All @@ -33,7 +31,7 @@ public class JvmMetricsInstaller implements AgentListener {
@Override
public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) {
ConfigProperties config = getConfig(autoConfiguredOpenTelemetrySdk);
boolean metricsEnabled = isProfilerMemoryEnabled(config);
boolean metricsEnabled = isProfilerMemoryEnabled();

if (!config.getBoolean("otel.instrumentation.jvm-metrics-splunk.enabled", metricsEnabled)) {
return;
Expand All @@ -43,12 +41,7 @@ public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetr
new OtelGcMemoryMetrics().install();
}

private boolean isProfilerMemoryEnabled(ConfigProperties config) {
ProfilerConfiguration profilerConfiguration =
ProfilerDeclarativeConfiguration.SUPPLIER.isConfigured()
? ProfilerDeclarativeConfiguration.SUPPLIER.get()
: new ProfilerEnvVarsConfiguration(config);

return profilerConfiguration.getMemoryEnabled();
private boolean isProfilerMemoryEnabled() {
return ProfilerConfiguration.SUPPLIER.get().getMemoryEnabled();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,27 @@
import static org.mockito.Mockito.verify;

import com.splunk.opentelemetry.instrumentation.jvmmetrics.otel.OtelAllocatedMemoryMetrics;
import com.splunk.opentelemetry.profiler.ProfilerConfiguration;
import com.splunk.opentelemetry.profiler.ProfilerEnvVarsConfiguration;
import com.splunk.opentelemetry.testing.declarativeconfig.DeclarativeConfigTestUtil;
import io.opentelemetry.instrumentation.config.bridge.DeclarativeConfigPropertiesBridgeBuilder;
import io.opentelemetry.sdk.autoconfigure.AutoConfigureUtil;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
import java.util.Map;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;

class JvmMetricsInstallerTest {

@AfterEach
void tearDown() {
ProfilerConfiguration.SUPPLIER.reset();
}

@Test
void shouldInstallJvmMetrics_declarativeConfig() {
// given
Expand All @@ -53,6 +61,7 @@ void shouldInstallJvmMetrics_declarativeConfig() {
new DeclarativeConfigPropertiesBridgeBuilder()
.build(
AutoConfigureUtil.getInstrumentationConfig(DeclarativeConfigTestUtil.parse(yaml)));
ProfilerConfiguration.SUPPLIER.configure(new ProfilerEnvVarsConfiguration(config));

try (MockedStatic<AutoConfigureUtil> autoConfigureUtil = mockStatic(AutoConfigureUtil.class);
MockedConstruction<OtelAllocatedMemoryMetrics> allocatedMetrics =
Expand All @@ -78,6 +87,7 @@ void shouldInstallJvmMetrics_envVarsConfig() {
ConfigProperties config =
DefaultConfigProperties.createFromMap(
Map.of("otel.instrumentation.jvm-metrics-splunk.enabled", "true"));
ProfilerConfiguration.SUPPLIER.configure(new ProfilerEnvVarsConfiguration(config));

try (MockedStatic<AutoConfigureUtil> autoConfigureUtil = mockStatic(AutoConfigureUtil.class);
MockedConstruction<OtelAllocatedMemoryMetrics> allocatedMetrics =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public static OpenTelemetryConfigurationModel getConfigurationModel() {
}

@VisibleForTesting
static void reset() {
public static void reset() {
configurationModel = null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
package com.splunk.opentelemetry.opamp;

import static io.opentelemetry.opamp.client.internal.request.service.HttpRequestService.DEFAULT_DELAY_BETWEEN_RETRIES;
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.opamp.effectiveconfig.EffectiveConfigReporter;
import com.splunk.opentelemetry.opamp.effectiveconfig.UpdatableEffectiveConfigState;
import com.splunk.opentelemetry.profiler.ProfilerConfiguration;
import com.splunk.opentelemetry.profiler.ProfilingSupervisor;
import io.opentelemetry.javaagent.extension.AgentListener;
import io.opentelemetry.opamp.client.OpampClient;
Expand All @@ -31,7 +33,6 @@
import io.opentelemetry.opamp.client.internal.request.service.HttpRequestService;
import io.opentelemetry.opamp.client.internal.response.MessageData;
import io.opentelemetry.opamp.client.internal.state.State;
import io.opentelemetry.sdk.autoconfigure.AutoConfigureUtil;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;
import java.io.IOException;
Expand All @@ -54,19 +55,24 @@ public void afterAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetr
return;
}

ProfilerRemoteConfiguration profilerRemoteConfiguration = setupProfilerConfiguration();
Resource resource = getResource(autoConfiguredOpenTelemetrySdk);
EffectiveConfigFactory effectiveConfigFactory =
createEffectiveConfigFactory(autoConfiguredOpenTelemetrySdk);
State.EffectiveConfig effectiveConfig = buildEffectiveConfig(effectiveConfigFactory);
UpdatableEffectiveConfigState effectiveConfigState = new UpdatableEffectiveConfigState();
EffectiveConfigReporter effectiveConfigReporter =
EffectiveConfigReporter.create(autoConfiguredOpenTelemetrySdk, effectiveConfigState);
effectiveConfigReporter.reportEffectiveConfigIfChanged();

ServerToAgentMessageHandler serverToAgentMessageHandler =
new ServerToAgentMessageHandler(ProfilingSupervisor.SUPPLIER.get());
new ServerToAgentMessageHandler(
profilerRemoteConfiguration,
ProfilingSupervisor.SUPPLIER.get(),
effectiveConfigReporter);

OpampClient client =
startOpampClient(
opampClientConfiguration,
resource,
effectiveConfig,
effectiveConfigState,
new OpampClient.Callbacks() {
@Override
public void onConnect(OpampClient opampClient) {
Expand Down Expand Up @@ -103,18 +109,20 @@ public void onMessage(OpampClient opampClient, MessageData messageData) {
}));
}

private ProfilerRemoteConfiguration setupProfilerConfiguration() {
ProfilerConfiguration originalConfig = ProfilerConfiguration.SUPPLIER.get();
ProfilerRemoteConfiguration remoteConfig = new ProfilerRemoteConfiguration(originalConfig);

ProfilerConfiguration.SUPPLIER.configure(remoteConfig);

return remoteConfig;
}

@Override
public int order() {
return Integer.MAX_VALUE;
}

private EffectiveConfigFactory createEffectiveConfigFactory(AutoConfiguredOpenTelemetrySdk sdk) {
if (AutoConfigureUtil.isDeclarativeConfig(sdk)) {
return new DeclarativeEffectiveConfigFileFactory();
}
return new EnvVarsEffectiveConfigFileFactory(getConfig(sdk));
}

static OpampClient startOpampClient(
OpampClientConfiguration opampClientConfiguration,
Resource resource,
Expand Down Expand Up @@ -146,15 +154,6 @@ static OpampClient startOpampClient(
return builder.build(callbacks);
}

static State.EffectiveConfig buildEffectiveConfig(EffectiveConfigFactory effectiveConfigFactory) {
return new State.EffectiveConfig() {
@Override
public opamp.proto.EffectiveConfig get() {
return new opamp.proto.EffectiveConfig(effectiveConfigFactory.createEffectiveConfigMap());
}
};
}

private static ComponentHealth createInitialHealthReport() {
Instant now = Instant.now();
long nowNanos = now.getEpochSecond() * 1_000_000_000L + now.getNano();
Expand Down

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[for reviewer] This class is a proxy over original configuration. Original profiler configuration is immutable once created. This class overwrites handling of the properties that are updatable via remote configuration, while preserving original values.
I'm still considering introducing a mutable ProfilerConfiguration interface implementation and letting appropriate factories to set it up. This would be served via SUPPLIER without any proxy objects.

Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* 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.opamp;

import com.splunk.opentelemetry.profiler.ProfilerConfiguration;
import java.time.Duration;
import java.util.Objects;
import java.util.logging.Logger;
import javax.annotation.Nullable;

public class ProfilerRemoteConfiguration implements ProfilerConfiguration {
private static final Logger logger =
Logger.getLogger(ProfilerRemoteConfiguration.class.getName());

private final ProfilerConfiguration delegate;

private boolean enabled;

ProfilerRemoteConfiguration(ProfilerConfiguration delegate) {
this.delegate = Objects.requireNonNull(delegate);

enabled = delegate.isEnabled();
}

void setEnabled(boolean enabled) {
this.enabled = enabled;
}

@Override
public boolean isEnabled() {
return enabled;
}

@Override
public void log() {
logger.info("-----------------------");
logger.info("Remote profiler configuration overwrites:");
log("Enabled", isEnabled());
logger.info("Base profiler configuration:");
delegate.log();
}

private static void log(String key, @Nullable Object value) {
logger.info(String.format("%30s : %s", key, value));
}

@Override
public String getIngestUrl() {
return delegate.getIngestUrl();
}

@Override
public String getOtlpProtocol() {
return delegate.getOtlpProtocol();
}

@Override
public boolean getMemoryEnabled() {
return delegate.getMemoryEnabled();
}

@Override
public boolean getMemoryEventRateLimitEnabled() {
return delegate.getMemoryEventRateLimitEnabled();
}

@Override
public String getMemoryEventRate() {
return delegate.getMemoryEventRate();
}

@Override
public boolean getUseAllocationSampleEvent() {
return delegate.getUseAllocationSampleEvent();
}

@Override
public Duration getCallStackInterval() {
return delegate.getCallStackInterval();
}

@Override
public boolean getIncludeAgentInternalStacks() {
return delegate.getIncludeAgentInternalStacks();
}

@Override
public boolean getIncludeJvmInternalStacks() {
return delegate.getIncludeJvmInternalStacks();
}

@Override
public boolean getTracingStacksOnly() {
return delegate.getTracingStacksOnly();
}

@Override
public int getStackDepth() {
return delegate.getStackDepth();
}

@Override
public boolean getKeepFiles() {
return delegate.getKeepFiles();
}

@Override
public String getProfilerDirectory() {
return delegate.getProfilerDirectory();
}

@Override
public Duration getRecordingDuration() {
return delegate.getRecordingDuration();
}

@Override
public Object getConfigProperties() {
return delegate.getConfigProperties();
}
}
Loading
Loading