diff --git a/README.md b/README.md index e9df9820..32dea901 100644 --- a/README.md +++ b/README.md @@ -29,18 +29,6 @@ of `pyroscope.jar` Visit [docs](https://pyroscope.io/docs/java/) page for usage and configuration documentation. -### Experimental OTLP Profiles export - -The agent can export async-profiler recordings using the experimental OpenTelemetry Profiles signal. Set -`PYROSCOPE_FORMAT=otlp` and configure `PYROSCOPE_SERVER_ADDRESS` with the base address of an OTLP/HTTP receiver. -The agent sends protobuf requests to `/v1development/profiles`. - -OTLP export requires the default `ASYNC` profiler and is not supported by the JFR profiler used on Windows. -Only one profiling event can run at a time. Allocation and lock thresholds can be configured when their event is -selected, for example with `PYROSCOPE_PROFILER_EVENT=alloc` and `PYROSCOPE_PROFILER_ALLOC=512k`. Multiple events -remain supported in sampling mode because they run sequentially. -The OpenTelemetry Profiles protocol and async-profiler output are experimental and may change incompatibly. - ## Building If you want to build the agent JAR yourself, from this repo run: diff --git a/agent/src/main/java/io/pyroscope/javaagent/AsyncProfilerDelegate.java b/agent/src/main/java/io/pyroscope/javaagent/AsyncProfilerDelegate.java index 1d1d135b..8bc5230b 100644 --- a/agent/src/main/java/io/pyroscope/javaagent/AsyncProfilerDelegate.java +++ b/agent/src/main/java/io/pyroscope/javaagent/AsyncProfilerDelegate.java @@ -147,6 +147,8 @@ private Snapshot dumpImpl(Instant started, Instant ended) { started, ended, data, + // This also removes closed scoped contexts. Keep draining them even though the + // current OTLP exporter does not include the resulting labels snapshot. Pyroscope.LabelsWrapper.dump() ); } diff --git a/agent/src/main/java/io/pyroscope/javaagent/PyroscopeAgent.java b/agent/src/main/java/io/pyroscope/javaagent/PyroscopeAgent.java index be5e518b..ed3710a2 100644 --- a/agent/src/main/java/io/pyroscope/javaagent/PyroscopeAgent.java +++ b/agent/src/main/java/io/pyroscope/javaagent/PyroscopeAgent.java @@ -1,5 +1,6 @@ package io.pyroscope.javaagent; +import io.pyroscope.http.Format; import io.pyroscope.javaagent.api.Exporter; import io.pyroscope.javaagent.api.Logger; import io.pyroscope.javaagent.api.ProfilingScheduler; @@ -59,6 +60,11 @@ public static void start(@NotNull Options options) { } sOptions = options; logger.log(Logger.Level.DEBUG, "Config: %s", options.config); + if (options.config.format == Format.OTLP) { + logger.log(Logger.Level.WARN, + "OTLP export does not include the configured application name or labels; " + + "profiles may appear under service_name=\"unknown_service\""); + } try { options.scheduler.start(options.profiler); ScopedContext.ENABLED.set(true); diff --git a/agent/src/test/java/io/pyroscope/javaagent/PyroscopeAgentTest.java b/agent/src/test/java/io/pyroscope/javaagent/PyroscopeAgentTest.java index c0713946..6a69fff2 100644 --- a/agent/src/test/java/io/pyroscope/javaagent/PyroscopeAgentTest.java +++ b/agent/src/test/java/io/pyroscope/javaagent/PyroscopeAgentTest.java @@ -1,5 +1,6 @@ package io.pyroscope.javaagent; +import io.pyroscope.http.Format; import io.pyroscope.javaagent.api.Logger; import io.pyroscope.javaagent.api.ProfilingScheduler; import io.pyroscope.javaagent.config.Config; @@ -27,6 +28,9 @@ public class PyroscopeAgentTest { @Mock private ProfilingScheduler profilingScheduler; + @Mock + private ProfilerDelegate profiler; + @BeforeEach void setUp() { configAgentEnabled = new Config.Builder() @@ -35,6 +39,7 @@ void setUp() { optionsAgentEnabled = new PyroscopeAgent.Options.Builder(configAgentEnabled) .setScheduler(profilingScheduler) .setLogger(logger) + .setProfiler(profiler) .build(); configAgentDisabled = new Config.Builder() @@ -43,6 +48,7 @@ void setUp() { optionsAgentDisabled = new PyroscopeAgent.Options.Builder(configAgentDisabled) .setScheduler(profilingScheduler) .setLogger(logger) + .setProfiler(profiler) .build(); } @@ -56,6 +62,7 @@ void startupTestWithEnabledAgent() { PyroscopeAgent.start(optionsAgentEnabled); verify(profilingScheduler, times(1)).start(any()); + verify(logger, never()).log(eq(Logger.Level.WARN), contains("OTLP export")); } @Test @@ -64,4 +71,24 @@ void startupTestWithDisabledAgent() { verify(profilingScheduler, never()).start(any()); } -} \ No newline at end of file + + @Test + void warnsWhenOtlpDoesNotIncludeApplicationNameOrLabels() { + Config config = new Config.Builder() + .setAgentEnabled(true) + .setFormat(Format.OTLP) + .build(); + PyroscopeAgent.Options options = new PyroscopeAgent.Options.Builder(config) + .setScheduler(profilingScheduler) + .setLogger(logger) + .setProfiler(profiler) + .build(); + + PyroscopeAgent.start(options); + + verify(logger).log( + Logger.Level.WARN, + "OTLP export does not include the configured application name or labels; " + + "profiles may appear under service_name=\"unknown_service\""); + } +}