You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2021/01/20 11:20:30 UTC
[camel] 05/06: CAMEL-16056: Added camel-jfr for java flight
recorder integration
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 3b112be98d34a06bd37112992625a878c9d198d7
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Jan 20 11:29:15 2021 +0100
CAMEL-16056: Added camel-jfr for java flight recorder integration
---
.../org/apache/camel/catalog/docs/jfr.adoc | 14 +--
components/camel-jfr/src/main/docs/jfr.adoc | 14 +--
.../jfr/FlightRecorderStartupStepRecorder.java | 88 ++++++++++++++++++
.../org/apache/camel/spi/StartupStepRecorder.java | 42 +++++++--
.../camel/impl/engine/AbstractCamelContext.java | 9 +-
.../MainConfigurationPropertiesConfigurer.java | 24 +++++
.../camel-main-configuration-metadata.json | 4 +
core/camel-main/src/main/docs/main.adoc | 4 +
.../org/apache/camel/main/BaseMainSupport.java | 73 ++++++++++++++-
.../camel/main/DefaultConfigurationConfigurer.java | 8 +-
.../camel/main/DefaultConfigurationProperties.java | 103 +++++++++++++++++++++
.../src/main/java/org/apache/camel/main/Main.java | 11 +--
.../startup/DefaultStartupStepRecorder.java | 55 ++++++++---
docs/components/modules/others/pages/jfr.adoc | 14 +--
14 files changed, 393 insertions(+), 70 deletions(-)
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jfr.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jfr.adoc
index 3a8fa2e..0b66da7 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jfr.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jfr.adoc
@@ -17,20 +17,8 @@ The camel-jfr component emits lifecycle events for startup to JFR.
This can for example be used to pin-point which Camel routes may be slow to startup.
[[jfr-Options]]
-== Options
-
-[width="100%",cols="10%,10%,80%",options="header",]
-|===
-|Option
-|Default
-|Description
-
-|enabled
-|true
-|Whether Camel flight recorder is enabled globally
-
-|===
+See the __startupRecorder__ options from xref:components:others:main.adoc[Camel Main]
[[jfr-Example]]
== Example
diff --git a/components/camel-jfr/src/main/docs/jfr.adoc b/components/camel-jfr/src/main/docs/jfr.adoc
index 3a8fa2e..0b66da7 100644
--- a/components/camel-jfr/src/main/docs/jfr.adoc
+++ b/components/camel-jfr/src/main/docs/jfr.adoc
@@ -17,20 +17,8 @@ The camel-jfr component emits lifecycle events for startup to JFR.
This can for example be used to pin-point which Camel routes may be slow to startup.
[[jfr-Options]]
-== Options
-
-[width="100%",cols="10%,10%,80%",options="header",]
-|===
-|Option
-|Default
-|Description
-
-|enabled
-|true
-|Whether Camel flight recorder is enabled globally
-
-|===
+See the __startupRecorder__ options from xref:components:others:main.adoc[Camel Main]
[[jfr-Example]]
== Example
diff --git a/components/camel-jfr/src/main/java/org/apache/camel/startup/jfr/FlightRecorderStartupStepRecorder.java b/components/camel-jfr/src/main/java/org/apache/camel/startup/jfr/FlightRecorderStartupStepRecorder.java
index 6c706a5..e9e4ac0 100644
--- a/components/camel-jfr/src/main/java/org/apache/camel/startup/jfr/FlightRecorderStartupStepRecorder.java
+++ b/components/camel-jfr/src/main/java/org/apache/camel/startup/jfr/FlightRecorderStartupStepRecorder.java
@@ -16,10 +16,22 @@
*/
package org.apache.camel.startup.jfr;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Duration;
+
+import jdk.jfr.Configuration;
+import jdk.jfr.FlightRecorder;
+import jdk.jfr.FlightRecorderListener;
+import jdk.jfr.Recording;
+import jdk.jfr.RecordingState;
import org.apache.camel.StartupStep;
import org.apache.camel.spi.StartupStepRecorder;
import org.apache.camel.spi.annotations.JdkService;
import org.apache.camel.support.startup.DefaultStartupStepRecorder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* To capture startup steps to be emitted to Java Flight Recorder.
@@ -27,8 +39,84 @@ import org.apache.camel.support.startup.DefaultStartupStepRecorder;
@JdkService(StartupStepRecorder.FACTORY)
public class FlightRecorderStartupStepRecorder extends DefaultStartupStepRecorder {
+ private static final Logger LOG = LoggerFactory.getLogger(FlightRecorderStartupStepRecorder.class);
+
+ private Recording rec;
+ private FlightRecorderListener frl;
+
+ @Override
+ public void doStart() throws Exception {
+ super.doStart();
+
+ if (isRecording()) {
+ FlightRecorder.register(FlightRecorderStartupStep.class);
+ Configuration config = Configuration.getConfiguration(getRecordingProfile());
+ rec = new Recording(config);
+ rec.setName("Camel Recording");
+ if (getStartupRecorderDuration() > 0) {
+ rec.setDuration(Duration.ofSeconds(getStartupRecorderDuration()));
+ LOG.info("Starting Java flight recorder with profile: {} and duration: {} seconds", getRecordingProfile(),
+ getStartupRecorderDuration());
+
+ // add listener to trigger auto-save when duration is hit
+ frl = new FlightRecorderListener() {
+ @Override
+ public void recordingStateChanged(Recording recording) {
+ if (recording == rec && recording.getState().equals(RecordingState.STOPPED)) {
+ LOG.info("Stopping Java flight recorder after {} seconds elapsed", getStartupRecorderDuration());
+ dumpRecording();
+ }
+ }
+ };
+ FlightRecorder.addListener(frl);
+ } else {
+ LOG.info("Starting Java flight recorder with profile: {}", getRecordingProfile());
+ }
+ rec.start();
+ }
+ }
+
+ @Override
+ public void doStop() throws Exception {
+ super.doStop();
+
+ if (rec != null) {
+ dumpRecording();
+ }
+ }
+
+ protected void dumpRecording() {
+ if (!"false".equals(getRecordingDir())) {
+ try {
+ Path dir = getRecordingDir() != null ? Paths.get(getRecordingDir()) : Paths.get(System.getenv().get("HOME"));
+ Path file = Files.createTempFile(dir, "camel-recording", ".jfr");
+ if (rec.getState().equals(RecordingState.RUNNING)) {
+ // need to do GC to capture details to the recording (specially when its short running)
+ LOG.info("Stopping Java flight recorder");
+ System.gc();
+ rec.stop();
+ }
+ if (rec.getState().equals(RecordingState.STOPPED)) {
+ rec.dump(file);
+ LOG.info("Java flight recorder saved to file: {}", file);
+ }
+ } catch (Exception e) {
+ LOG.warn("Error saving Java flight recorder recording to file", e);
+ }
+ }
+ FlightRecorder.unregister(FlightRecorderStartupStep.class);
+ if (frl != null) {
+ FlightRecorder.removeListener(frl);
+ }
+
+ rec = null;
+ frl = null;
+ }
+
public FlightRecorderStartupStepRecorder() {
setEnabled(true);
+ // pre-empty enable recording so we have as early as possible recording started
+ setRecording(true);
}
@Override
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/StartupStepRecorder.java b/core/camel-api/src/main/java/org/apache/camel/spi/StartupStepRecorder.java
index f0e3918..be3f68a 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/StartupStepRecorder.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/StartupStepRecorder.java
@@ -41,16 +41,36 @@ public interface StartupStepRecorder extends StaticService {
void setEnabled(boolean enabled);
/**
- * Whether to automatic disable this recorder after Camel has been started. This is done by default to remove any
- * overhead after the startup process is done.
+ * How long time to run the startup recorder.
+ *
+ * Use 0 (default) to stop the recorder after Camel has been started. Use -1 to keep the recorder running until
+ * Camel is being stopped. A positive value is to run the recorder for N seconds.
*/
- boolean isDisableAfterStarted();
+ long getStartupRecorderDuration();
/**
- * Whether to automatic disable this recorder after Camel has been started. This is done by default to remove any
- * overhead after the startup process is done.
+ * How long time to run the startup recorder.
+ *
+ * Use 0 (default) to stop the recorder after Camel has been started. Use -1 to keep the recorder running until
+ * Camel is being stopped. A positive value is to run the recorder for N seconds.
*/
- void setDisableAfterStarted(boolean disableAfterStarted);
+ void setStartupRecorderDuration(long startupRecorderDuration);
+
+ String getRecordingDir();
+
+ /**
+ * Directory to store the recording. By default the user home directory will be used.
+ */
+ void setRecordingDir(String recordingDir);
+
+ String getRecordingProfile();
+
+ /**
+ * To use a specific Java Flight Recorder profile configuration, such as default or profile.
+ *
+ * The default is default.
+ */
+ void setRecordingProfile(String profile);
/**
* To filter our sub steps at a maximum depth
@@ -63,6 +83,16 @@ public interface StartupStepRecorder extends StaticService {
int getMaxDepth();
/**
+ * Whether to start flight recorder recording. This is only in use if camel-jfr is being used.
+ */
+ void setRecording(boolean recording);
+
+ /**
+ * Whether to start flight recorder recording. This is only in use if camel-jfr is being used.
+ */
+ boolean isRecording();
+
+ /**
* Beings a new step.
* <p>
* Important must call {@link #endStep(StartupStep)} to end the step.
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
index 646bca5..91a1b36 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/AbstractCamelContext.java
@@ -2528,8 +2528,8 @@ public abstract class AbstractCamelContext extends BaseService
public void doBuild() throws Exception {
bootDate = System.currentTimeMillis();
- // auto-detect step recorder from classpath if not explicit configured
- if (startupStepRecorder instanceof DefaultStartupStepRecorder) {
+ // auto-detect step recorder from classpath if none has been explicit configured
+ if (startupStepRecorder.getClass().getSimpleName().equals("DefaultStartupStepRecorder")) {
StartupStepRecorder fr = getBootstrapFactoryFinder()
.newInstance(StartupStepRecorder.FACTORY, StartupStepRecorder.class).orElse(null);
if (fr != null) {
@@ -2742,7 +2742,7 @@ public abstract class AbstractCamelContext extends BaseService
startupStepRecorder.endStep(step);
- if (startupStepRecorder.isDisableAfterStarted()) {
+ if (startupStepRecorder.getStartupRecorderDuration() == 0) {
startupStepRecorder.stop();
}
}
@@ -3144,6 +3144,9 @@ public abstract class AbstractCamelContext extends BaseService
TimeUtils.printDuration(stopWatch.taken()));
}
+ // ensure any recorder is stopped in case it was kept running
+ startupStepRecorder.stop();
+
// and clear start date
startDate = 0;
bootDate = 0;
diff --git a/core/camel-main/src/generated/java/org/apache/camel/main/MainConfigurationPropertiesConfigurer.java b/core/camel-main/src/generated/java/org/apache/camel/main/MainConfigurationPropertiesConfigurer.java
index 0ec603e..63940e0 100644
--- a/core/camel-main/src/generated/java/org/apache/camel/main/MainConfigurationPropertiesConfigurer.java
+++ b/core/camel-main/src/generated/java/org/apache/camel/main/MainConfigurationPropertiesConfigurer.java
@@ -145,8 +145,16 @@ public class MainConfigurationPropertiesConfigurer extends org.apache.camel.supp
case "ShutdownTimeout": target.setShutdownTimeout(property(camelContext, int.class, value)); return true;
case "startuprecorder":
case "StartupRecorder": target.setStartupRecorder(property(camelContext, java.lang.String.class, value)); return true;
+ case "startuprecorderdir":
+ case "StartupRecorderDir": target.setStartupRecorderDir(property(camelContext, java.lang.String.class, value)); return true;
+ case "startuprecorderduration":
+ case "StartupRecorderDuration": target.setStartupRecorderDuration(property(camelContext, long.class, value)); return true;
case "startuprecordermaxdepth":
case "StartupRecorderMaxDepth": target.setStartupRecorderMaxDepth(property(camelContext, int.class, value)); return true;
+ case "startuprecorderprofile":
+ case "StartupRecorderProfile": target.setStartupRecorderProfile(property(camelContext, java.lang.String.class, value)); return true;
+ case "startuprecorderrecording":
+ case "StartupRecorderRecording": target.setStartupRecorderRecording(property(camelContext, boolean.class, value)); return true;
case "streamcachinganyspoolrules":
case "StreamCachingAnySpoolRules": target.setStreamCachingAnySpoolRules(property(camelContext, boolean.class, value)); return true;
case "streamcachingbuffersize":
@@ -316,8 +324,16 @@ public class MainConfigurationPropertiesConfigurer extends org.apache.camel.supp
case "ShutdownTimeout": return int.class;
case "startuprecorder":
case "StartupRecorder": return java.lang.String.class;
+ case "startuprecorderdir":
+ case "StartupRecorderDir": return java.lang.String.class;
+ case "startuprecorderduration":
+ case "StartupRecorderDuration": return long.class;
case "startuprecordermaxdepth":
case "StartupRecorderMaxDepth": return int.class;
+ case "startuprecorderprofile":
+ case "StartupRecorderProfile": return java.lang.String.class;
+ case "startuprecorderrecording":
+ case "StartupRecorderRecording": return boolean.class;
case "streamcachinganyspoolrules":
case "StreamCachingAnySpoolRules": return boolean.class;
case "streamcachingbuffersize":
@@ -488,8 +504,16 @@ public class MainConfigurationPropertiesConfigurer extends org.apache.camel.supp
case "ShutdownTimeout": return target.getShutdownTimeout();
case "startuprecorder":
case "StartupRecorder": return target.getStartupRecorder();
+ case "startuprecorderdir":
+ case "StartupRecorderDir": return target.getStartupRecorderDir();
+ case "startuprecorderduration":
+ case "StartupRecorderDuration": return target.getStartupRecorderDuration();
case "startuprecordermaxdepth":
case "StartupRecorderMaxDepth": return target.getStartupRecorderMaxDepth();
+ case "startuprecorderprofile":
+ case "StartupRecorderProfile": return target.getStartupRecorderProfile();
+ case "startuprecorderrecording":
+ case "StartupRecorderRecording": return target.isStartupRecorderRecording();
case "streamcachinganyspoolrules":
case "StreamCachingAnySpoolRules": return target.isStreamCachingAnySpoolRules();
case "streamcachingbuffersize":
diff --git a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
index 9429f05..640fe32 100644
--- a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
+++ b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
@@ -72,7 +72,11 @@
{ "name": "camel.main.shutdownSuppressLoggingOnTimeout", "description": "Whether Camel should try to suppress logging during shutdown and timeout was triggered, meaning forced shutdown is happening. And during forced shutdown we want to avoid logging errors\/warnings et all in the logs as a side-effect of the forced timeout. Notice the suppress is a best effort as there may still be some logs coming from 3rd party libraries and whatnot, which Camel cannot control. This option is defa [...]
{ "name": "camel.main.shutdownTimeout", "description": "Timeout in seconds to graceful shutdown Camel.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "integer", "javaType": "int", "defaultValue": 45 },
{ "name": "camel.main.startupRecorder", "description": "To use startup recorder for capturing execution time during starting Camel. The recorder can be one of: false, logging, java-flight-recorder The default is false.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "string", "javaType": "java.lang.String" },
+ { "name": "camel.main.startupRecorderDir", "description": "Directory to store the recording. By default the user home directory will be used. Use false to turn off saving recording to disk.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "string", "javaType": "java.lang.String" },
+ { "name": "camel.main.startupRecorderDuration", "description": "How long time to run the startup recorder. Use 0 (default) to stop the recorder after Camel has been started. Use -1 to keep the recorder running until Camel is being stopped. A positive value is to run the recorder for N seconds. When the recorder is stopped then the recording is auto saved to disk", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "integer", "javaType": "long" },
{ "name": "camel.main.startupRecorderMaxDepth", "description": "To filter our sub steps at a maximum depth. Use -1 for no maximum. Use 0 for no sub steps. Use 1 for max 1 sub step, and so forth. The default is -1.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "integer", "javaType": "int", "defaultValue": -1 },
+ { "name": "camel.main.startupRecorderProfile", "description": "To use a specific Java Flight Recorder profile configuration, such as default or profile. The default is default.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "string", "javaType": "java.lang.String", "defaultValue": "default" },
+ { "name": "camel.main.startupRecorderRecording", "description": "To enable Java Flight Recorder to start a recording and automatic dump the recording to disk after startup is complete. This requires that camel-jfr is on the classpath. The default is true.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": true },
{ "name": "camel.main.streamCachingAnySpoolRules", "description": "Sets whether if just any of the org.apache.camel.spi.StreamCachingStrategy.SpoolRule rules returns true then shouldSpoolCache(long) returns true, to allow spooling to disk. If this option is false, then all the org.apache.camel.spi.StreamCachingStrategy.SpoolRule must return true. The default value is false which means that all the rules must return true.", "sourceType": "org.apache.camel.main.DefaultConfigurationProp [...]
{ "name": "camel.main.streamCachingBufferSize", "description": "Sets the stream caching buffer size to use when allocating in-memory buffers used for in-memory stream caches. The default size is 4096.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "integer", "javaType": "int" },
{ "name": "camel.main.streamCachingEnabled", "description": "Sets whether stream caching is enabled or not. Default is false.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean", "javaType": "boolean" },
diff --git a/core/camel-main/src/main/docs/main.adoc b/core/camel-main/src/main/docs/main.adoc
index 7c60056..f00aef5 100644
--- a/core/camel-main/src/main/docs/main.adoc
+++ b/core/camel-main/src/main/docs/main.adoc
@@ -84,7 +84,11 @@ The following table lists all the options:
| *camel.main.shutdownSuppress{zwsp}LoggingOnTimeout* | Whether Camel should try to suppress logging during shutdown and timeout was triggered, meaning forced shutdown is happening. And during forced shutdown we want to avoid logging errors/warnings et all in the logs as a side-effect of the forced timeout. Notice the suppress is a best effort as there may still be some logs coming from 3rd party libraries and whatnot, which Camel cannot control. This option is default false. | | boolean
| *camel.main.shutdownTimeout* | Timeout in seconds to graceful shutdown Camel. | 45 | int
| *camel.main.startupRecorder* | To use startup recorder for capturing execution time during starting Camel. The recorder can be one of: false, logging, java-flight-recorder The default is false. | | String
+| *camel.main.startupRecorderDir* | Directory to store the recording. By default the user home directory will be used. Use false to turn off saving recording to disk. | | String
+| *camel.main.startupRecorder{zwsp}Duration* | How long time to run the startup recorder. Use 0 (default) to stop the recorder after Camel has been started. Use -1 to keep the recorder running until Camel is being stopped. A positive value is to run the recorder for N seconds. When the recorder is stopped then the recording is auto saved to disk | | long
| *camel.main.startupRecorderMax{zwsp}Depth* | To filter our sub steps at a maximum depth. Use -1 for no maximum. Use 0 for no sub steps. Use 1 for max 1 sub step, and so forth. The default is -1. | -1 | int
+| *camel.main.startupRecorder{zwsp}Profile* | To use a specific Java Flight Recorder profile configuration, such as default or profile. The default is default. | default | String
+| *camel.main.startupRecorder{zwsp}Recording* | To enable Java Flight Recorder to start a recording and automatic dump the recording to disk after startup is complete. This requires that camel-jfr is on the classpath. The default is true. | true | boolean
| *camel.main.streamCachingAny{zwsp}SpoolRules* | Sets whether if just any of the org.apache.camel.spi.StreamCachingStrategy.SpoolRule rules returns true then shouldSpoolCache(long) returns true, to allow spooling to disk. If this option is false, then all the org.apache.camel.spi.StreamCachingStrategy.SpoolRule must return true. The default value is false which means that all the rules must return true. | | boolean
| *camel.main.streamCachingBuffer{zwsp}Size* | Sets the stream caching buffer size to use when allocating in-memory buffers used for in-memory stream caches. The default size is 4096. | | int
| *camel.main.streamCaching{zwsp}Enabled* | Sets whether stream caching is enabled or not. Default is false. | | boolean
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
index 1eb9f1d..c3ea133 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
@@ -51,11 +51,13 @@ import org.apache.camel.spi.DataFormat;
import org.apache.camel.spi.Language;
import org.apache.camel.spi.PropertiesComponent;
import org.apache.camel.spi.RouteTemplateParameterSource;
+import org.apache.camel.spi.StartupStepRecorder;
import org.apache.camel.support.CamelContextHelper;
import org.apache.camel.support.LifecycleStrategySupport;
import org.apache.camel.support.PropertyBindingSupport;
import org.apache.camel.support.ResourceHelper;
import org.apache.camel.support.service.BaseService;
+import org.apache.camel.support.startup.LoggingStartupStepRecorder;
import org.apache.camel.util.FileUtil;
import org.apache.camel.util.IOHelper;
import org.apache.camel.util.ObjectHelper;
@@ -440,6 +442,70 @@ public abstract class BaseMainSupport extends BaseService {
}
}
+ protected void configureStartupRecorder(CamelContext camelContext) {
+ // we need to load these configurations early as they control the startup recorder when using camel-jfr
+ // and we want to start jfr recording as early as possible to also capture details during bootstrapping Camel
+
+ // load properties
+ Properties prop = camelContext.getPropertiesComponent().loadProperties(name -> name.startsWith("camel."));
+
+ Object value = prop.remove("camel.main.startupRecorder");
+ if (value == null) {
+ value = prop.remove("camel.main.startup-recorder");
+ if (value != null) {
+ mainConfigurationProperties.setStartupRecorder(value.toString());
+ }
+ }
+ value = prop.remove("camel.main.startupRecorderRecording");
+ if (value == null) {
+ value = prop.remove("camel.main.startup-recorder-recording");
+ if (value != null) {
+ mainConfigurationProperties.setStartupRecorderRecording("true".equalsIgnoreCase(value.toString()));
+ }
+ }
+ value = prop.remove("camel.main.startupRecorderProfile");
+ if (value == null) {
+ value = prop.remove("camel.main.startup-recorder-profile");
+ if (value != null) {
+ mainConfigurationProperties.setStartupRecorderProfile(
+ CamelContextHelper.parseText(camelContext, value.toString()));
+ }
+ }
+ value = prop.remove("camel.main.startupRecorderDuration");
+ if (value == null) {
+ value = prop.remove("camel.main.startup-recorder-duration");
+ if (value != null) {
+ mainConfigurationProperties.setStartupRecorderDuration(Long.parseLong(value.toString()));
+ }
+ }
+ value = prop.remove("camel.main.startupRecorderMaxDepth");
+ if (value == null) {
+ value = prop.remove("camel.main.startup-recorder-max-depth");
+ if (value != null) {
+ mainConfigurationProperties.setStartupRecorderMaxDepth(Integer.parseInt(value.toString()));
+ }
+ }
+
+ if ("false".equals(mainConfigurationProperties.getStartupRecorder())) {
+ camelContext.adapt(ExtendedCamelContext.class).getStartupStepRecorder().setEnabled(false);
+ } else if ("logging".equals(mainConfigurationProperties.getStartupRecorder())) {
+ camelContext.adapt(ExtendedCamelContext.class).setStartupStepRecorder(new LoggingStartupStepRecorder());
+ } else if ("java-flight-recorder".equals(mainConfigurationProperties.getStartupRecorder())
+ || mainConfigurationProperties.getStartupRecorder() == null) {
+ // try to auto discover camel-jfr to use
+ StartupStepRecorder fr = camelContext.adapt(ExtendedCamelContext.class).getBootstrapFactoryFinder()
+ .newInstance(StartupStepRecorder.FACTORY, StartupStepRecorder.class).orElse(null);
+ if (fr != null) {
+ LOG.debug("Discovered startup recorder: {} from classpath", fr);
+ fr.setRecording(mainConfigurationProperties.isStartupRecorderRecording());
+ fr.setStartupRecorderDuration(mainConfigurationProperties.getStartupRecorderDuration());
+ fr.setRecordingProfile(mainConfigurationProperties.getStartupRecorderProfile());
+ fr.setMaxDepth(mainConfigurationProperties.getStartupRecorderMaxDepth());
+ camelContext.adapt(ExtendedCamelContext.class).setStartupStepRecorder(fr);
+ }
+ }
+ }
+
protected void configureRoutes(CamelContext camelContext) throws Exception {
// try to load the route builders
loadRouteBuilders(camelContext);
@@ -450,6 +516,11 @@ public abstract class BaseMainSupport extends BaseService {
}
protected void postProcessCamelContext(CamelContext camelContext) throws Exception {
+ // setup properties
+ configurePropertiesService(camelContext);
+ // setup startup recorder before building context
+ configureStartupRecorder(camelContext);
+
// ensure camel is initialized
camelContext.build();
@@ -457,8 +528,6 @@ public abstract class BaseMainSupport extends BaseService {
listener.beforeInitialize(this);
}
- configurePropertiesService(camelContext);
-
// allow to do configuration before its started
for (MainListener listener : listeners) {
listener.beforeConfigure(this);
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationConfigurer.java b/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationConfigurer.java
index 13ae341..d2481db 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationConfigurer.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationConfigurer.java
@@ -96,7 +96,9 @@ public final class DefaultConfigurationConfigurer {
if ("false".equals(config.getStartupRecorder())) {
ecc.getStartupStepRecorder().setEnabled(false);
} else if ("logging".equals(config.getStartupRecorder())) {
- ecc.setStartupStepRecorder(new LoggingStartupStepRecorder());
+ if (!(ecc.getStartupStepRecorder() instanceof LoggingStartupStepRecorder)) {
+ ecc.setStartupStepRecorder(new LoggingStartupStepRecorder());
+ }
} else if ("java-flight-recorder".equals(config.getStartupRecorder())) {
if (!ecc.getStartupStepRecorder().getClass().getName().startsWith("org.apache.camel.startup.jfr"))
throw new IllegalArgumentException(
@@ -104,6 +106,10 @@ public final class DefaultConfigurationConfigurer {
}
}
ecc.getStartupStepRecorder().setMaxDepth(config.getStartupRecorderMaxDepth());
+ ecc.getStartupStepRecorder().setRecording(config.isStartupRecorderRecording());
+ ecc.getStartupStepRecorder().setStartupRecorderDuration(config.getStartupRecorderDuration());
+ ecc.getStartupStepRecorder().setRecordingDir(config.getStartupRecorderDir());
+ ecc.getStartupStepRecorder().setRecordingProfile(config.getStartupRecorderProfile());
ecc.setLightweight(config.isLightweight());
ecc.getBeanPostProcessor().setEnabled(config.isBeanPostProcessorEnabled());
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java
index 8b71105..070b9f9 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationProperties.java
@@ -102,6 +102,10 @@ public abstract class DefaultConfigurationProperties<T> {
private boolean routeControllerUnhealthyOnExhausted;
private String startupRecorder;
private int startupRecorderMaxDepth = -1;
+ private boolean startupRecorderRecording = true;
+ private String startupRecorderProfile = "default";
+ private long startupRecorderDuration;
+ private String startupRecorderDir;
// getter and setters
// --------------------------------------------------------------
@@ -1127,6 +1131,63 @@ public abstract class DefaultConfigurationProperties<T> {
this.startupRecorderMaxDepth = startupRecorderMaxDepth;
}
+ public boolean isStartupRecorderRecording() {
+ return startupRecorderRecording;
+ }
+
+ /**
+ * To enable Java Flight Recorder to start a recording and automatic dump the recording to disk after startup is
+ * complete.
+ *
+ * This requires that camel-jfr is on the classpath.
+ *
+ * The default is true.
+ */
+ public void setStartupRecorderRecording(boolean startupRecorderRecording) {
+ this.startupRecorderRecording = startupRecorderRecording;
+ }
+
+ public String getStartupRecorderProfile() {
+ return startupRecorderProfile;
+ }
+
+ /**
+ * To use a specific Java Flight Recorder profile configuration, such as default or profile.
+ *
+ * The default is default.
+ */
+ public void setStartupRecorderProfile(String startupRecorderProfile) {
+ this.startupRecorderProfile = startupRecorderProfile;
+ }
+
+ public long getStartupRecorderDuration() {
+ return startupRecorderDuration;
+ }
+
+ /**
+ * How long time to run the startup recorder.
+ *
+ * Use 0 (default) to stop the recorder after Camel has been started. Use -1 to keep the recorder running until
+ * Camel is being stopped. A positive value is to run the recorder for N seconds.
+ *
+ * When the recorder is stopped then the recording is auto saved to disk
+ */
+ public void setStartupRecorderDuration(long startupRecorderDuration) {
+ this.startupRecorderDuration = startupRecorderDuration;
+ }
+
+ public String getStartupRecorderDir() {
+ return startupRecorderDir;
+ }
+
+ /**
+ * Directory to store the recording. By default the user home directory will be used. Use false to turn off saving
+ * recording to disk.
+ */
+ public void setStartupRecorderDir(String startupRecorderDir) {
+ this.startupRecorderDir = startupRecorderDir;
+ }
+
// fluent builders
// --------------------------------------------------------------
@@ -1901,4 +1962,46 @@ public abstract class DefaultConfigurationProperties<T> {
return (T) this;
}
+ /**
+ * To enable Java Flight Recorder to start a recording and automatic dump the recording to disk after startup is
+ * complete.
+ *
+ * This requires that camel-jfr is in use.
+ *
+ * The default is false.
+ */
+ public T withStartupRecorderRecording(boolean startupRecorderRecording) {
+ this.startupRecorderRecording = startupRecorderRecording;
+ return (T) this;
+ }
+
+ /**
+ * To use a specific Java Flight Recorder profile configuration, such as default or profile.
+ *
+ * The default is default.
+ */
+ public T withStartupRecorderProfile(String startupRecorderProfile) {
+ this.startupRecorderProfile = startupRecorderProfile;
+ return (T) this;
+ }
+
+ /**
+ * How long time to run the startup recorder.
+ *
+ * Use 0 (default) to stop the recorder after Camel has been started. Use -1 to keep the recorder running until
+ * Camel is being stopped. A positive value is to run the recorder for N seconds.
+ */
+ public T withStartupRecorderDuration(long startupRecorderDuration) {
+ this.startupRecorderDuration = startupRecorderDuration;
+ return (T) this;
+ }
+
+ /**
+ * Directory to store the recording. By default the user home directory will be used.
+ */
+ public T withStartupRecorderDir(String startupRecorderDir) {
+ this.startupRecorderDir = startupRecorderDir;
+ return (T) this;
+ }
+
}
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/Main.java b/core/camel-main/src/main/java/org/apache/camel/main/Main.java
index af29695..fcc4633 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/Main.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/Main.java
@@ -139,13 +139,10 @@ public class Main extends MainCommandLineSupport {
@Override
protected CamelContext createCamelContext() {
- return new DefaultCamelContext(registry);
- // TODO: LightweightCamelContext is not ready yet
- //if (mainConfigurationProperties.isLightweight()) {
- // return new LightweightCamelContext(registry);
- //} else {
- // return new DefaultCamelContext(registry);
- //}
+ // do not build/init camel context yet
+ DefaultCamelContext answer = new DefaultCamelContext(false);
+ answer.setRegistry(registry);
+ return answer;
}
}
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/startup/DefaultStartupStepRecorder.java b/core/camel-support/src/main/java/org/apache/camel/support/startup/DefaultStartupStepRecorder.java
index 67a8bff..d2e3b9b 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/startup/DefaultStartupStepRecorder.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/startup/DefaultStartupStepRecorder.java
@@ -22,11 +22,12 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.apache.camel.StartupStep;
import org.apache.camel.spi.StartupStepRecorder;
+import org.apache.camel.support.service.ServiceSupport;
/**
* Default {@link StartupStepRecorder} that is always disabled.
*/
-public class DefaultStartupStepRecorder implements StartupStepRecorder {
+public class DefaultStartupStepRecorder extends ServiceSupport implements StartupStepRecorder {
private final AtomicInteger stepCounter = new AtomicInteger();
private final Deque<Integer> currentSteps = new ArrayDeque<>();
@@ -74,13 +75,12 @@ public class DefaultStartupStepRecorder implements StartupStepRecorder {
};
- public DefaultStartupStepRecorder() {
- currentSteps.offerFirst(0);
- }
-
private boolean enabled;
- private boolean disableAfterStarted = true;
private int maxDepth = -1;
+ private long startupRecorderDuration;
+ private boolean recording;
+ private String recordingDir;
+ private String recordingProfile = "default";
public boolean isEnabled() {
return enabled;
@@ -91,12 +91,13 @@ public class DefaultStartupStepRecorder implements StartupStepRecorder {
}
@Override
- public boolean isDisableAfterStarted() {
- return disableAfterStarted;
+ public long getStartupRecorderDuration() {
+ return startupRecorderDuration;
}
- public void setDisableAfterStarted(boolean disableAfterStarted) {
- this.disableAfterStarted = disableAfterStarted;
+ @Override
+ public void setStartupRecorderDuration(long startupRecorderDuration) {
+ this.startupRecorderDuration = startupRecorderDuration;
}
@Override
@@ -109,12 +110,42 @@ public class DefaultStartupStepRecorder implements StartupStepRecorder {
}
@Override
- public void start() {
+ public boolean isRecording() {
+ return recording;
+ }
+
+ @Override
+ public void setRecording(boolean recording) {
+ this.recording = recording;
+ }
+
+ @Override
+ public String getRecordingDir() {
+ return recordingDir;
+ }
+
+ @Override
+ public void setRecordingDir(String recordingDir) {
+ this.recordingDir = recordingDir;
+ }
+
+ @Override
+ public String getRecordingProfile() {
+ return recordingProfile;
+ }
+
+ @Override
+ public void setRecordingProfile(String recordingProfile) {
+ this.recordingProfile = recordingProfile;
+ }
+
+ @Override
+ protected void doStart() throws Exception {
currentSteps.offerFirst(0);
}
@Override
- public void stop() {
+ public void doStop() throws Exception {
enabled = false;
currentSteps.clear();
}
diff --git a/docs/components/modules/others/pages/jfr.adoc b/docs/components/modules/others/pages/jfr.adoc
index f9f2af7..45f5abb 100644
--- a/docs/components/modules/others/pages/jfr.adoc
+++ b/docs/components/modules/others/pages/jfr.adoc
@@ -19,20 +19,8 @@ The camel-jfr component emits lifecycle events for startup to JFR.
This can for example be used to pin-point which Camel routes may be slow to startup.
[[jfr-Options]]
-== Options
-
-[width="100%",cols="10%,10%,80%",options="header",]
-|===
-|Option
-|Default
-|Description
-
-|enabled
-|true
-|Whether Camel flight recorder is enabled globally
-
-|===
+See the __startupRecorder__ options from xref:components:others:main.adoc[Camel Main]
[[jfr-Example]]
== Example