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 2025/12/04 15:58:52 UTC

(camel) branch cd created (now cbb75e30bf1e)

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a change to branch cd
in repository https://gitbox.apache.org/repos/asf/camel.git


      at cbb75e30bf1e CAMEL-22748: camel-jbang - camel debug to do remote attach to existing running Camel

This branch includes the following new commits:

     new cbb75e30bf1e CAMEL-22748: camel-jbang - camel debug to do remote attach to existing running Camel

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(camel) 01/01: CAMEL-22748: camel-jbang - camel debug to do remote attach to existing running Camel

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch cd
in repository https://gitbox.apache.org/repos/asf/camel.git

commit cbb75e30bf1ee48ee4c8c8fdb2c2dbb6cce25082
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Dec 4 16:58:17 2025 +0100

    CAMEL-22748: camel-jbang - camel debug to do remote attach to existing running Camel
---
 bom/camel-bom/pom.xml                              |   5 +
 .../org/apache/camel/catalog/others.properties     |   1 +
 .../org/apache/camel/catalog/others/cli-debug.json |  15 +++
 .../java/org/apache/camel/spi/CliConnector.java    |   7 ++
 .../org/apache/camel/spi/ShutdownPrepared.java     |   6 +-
 .../impl/debugger/DefaultBacklogDebugger.java      |  12 +-
 .../camel/impl/engine/AbstractCamelContext.java    |  23 ++--
 docs/components/modules/others/nav.adoc            |   1 +
 .../components/modules/others/pages/cli-debug.adoc |   1 +
 .../camel/cli/connector/LocalCliConnector.java     |  50 +++++++-
 dsl/{ => camel-cli-debug}/pom.xml                  |  61 ++++------
 .../services/org/apache/camel/debugger-factory     |   2 +
 .../services/org/apache/camel/other.properties     |   7 ++
 .../src/generated/resources/cli-debug.json         |  15 +++
 dsl/camel-cli-debug/src/main/docs/cli-debug.adoc   |  55 +++++++++
 .../cli/debug/CamelCliDebuggerFactory.java         | 127 +++++++++++++++++++++
 .../camel/dsl/jbang/core/commands/Debug.java       | 119 ++++++++++++++++++-
 dsl/pom.xml                                        |   1 +
 parent/pom.xml                                     |   5 +
 19 files changed, 451 insertions(+), 62 deletions(-)

diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml
index 10a4c83ec8eb..710d1a661cba 100644
--- a/bom/camel-bom/pom.xml
+++ b/bom/camel-bom/pom.xml
@@ -441,6 +441,11 @@
         <artifactId>camel-cli-connector</artifactId>
         <version>4.17.0-SNAPSHOT</version>
       </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-cli-debug</artifactId>
+        <version>4.17.0-SNAPSHOT</version>
+      </dependency>
       <dependency>
         <groupId>org.apache.camel</groupId>
         <artifactId>camel-clickup</artifactId>
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others.properties b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others.properties
index ed5acbb0859e..964ecb3fc0d7 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others.properties
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others.properties
@@ -2,6 +2,7 @@ attachments
 aws-xray
 azure-schema-registry
 cli-connector
+cli-debug
 cloud
 cloudevents
 cluster
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others/cli-debug.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others/cli-debug.json
new file mode 100644
index 000000000000..4acfc083d275
--- /dev/null
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others/cli-debug.json
@@ -0,0 +1,15 @@
+{
+  "other": {
+    "kind": "other",
+    "name": "cli-debug",
+    "title": "CLI Debug",
+    "description": "Remote CLI debugger",
+    "deprecated": false,
+    "firstVersion": "4.17.0",
+    "label": "tooling",
+    "supportLevel": "Preview",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-cli-debug",
+    "version": "4.17.0-SNAPSHOT"
+  }
+}
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/CliConnector.java b/core/camel-api/src/main/java/org/apache/camel/spi/CliConnector.java
index ded99a33e065..36997762846b 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/CliConnector.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/CliConnector.java
@@ -31,4 +31,11 @@ public interface CliConnector extends StaticService, NonManagedService {
      */
     void sigterm();
 
+    /**
+     * Allows to adjust the frequency delay which the CliConnect reacts. Uses 1000 millis by default.
+     *
+     * @param delay delay in millis
+     */
+    void updateDelay(int delay);
+
 }
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/ShutdownPrepared.java b/core/camel-api/src/main/java/org/apache/camel/spi/ShutdownPrepared.java
index 60f2c8c246e0..666dc38abdcf 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/ShutdownPrepared.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/ShutdownPrepared.java
@@ -37,7 +37,7 @@ public interface ShutdownPrepared {
      * {@link ShutdownStrategy} performs a more aggressive shutdown, calling this method a second time with
      * <tt>true</tt> for the given forced parameter. For example by graceful stopping any threads or the likes.
      * <p/>
-     * In addition a service can also be suspended (not stopped), and when this happens the parameter
+     * In addition, a service can also be suspended (not stopped), and when this happens the parameter
      * <tt>suspendOnly</tt> has the value <tt>true</tt>. This can be used to prepare the service for suspension, such as
      * marking a worker thread to skip action.
      * <p/>
@@ -46,8 +46,8 @@ public interface ShutdownPrepared {
      *
      * @param suspendOnly <tt>true</tt> if the intention is to only suspend the service, and not stop/shutdown the
      *                    service.
-     * @param forced      <tt>true</tt> is forcing a more aggressive shutdown, <tt>false</tt> is for preparing to
-     *                    shutdown.
+     * @param forced      <tt>true</tt> is forcing a more aggressive shutdown, <tt>false</tt> is for preparing to shut
+     *                    down.
      */
     void prepareShutdown(boolean suspendOnly, boolean forced);
 
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogDebugger.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogDebugger.java
index 2d41f81a70a4..92daba25a46d 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogDebugger.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DefaultBacklogDebugger.java
@@ -50,6 +50,7 @@ import org.apache.camel.spi.CamelEvent.ExchangeEvent;
 import org.apache.camel.spi.CamelLogger;
 import org.apache.camel.spi.Condition;
 import org.apache.camel.spi.Debugger;
+import org.apache.camel.spi.ShutdownPrepared;
 import org.apache.camel.support.BreakpointSupport;
 import org.apache.camel.support.CamelContextHelper;
 import org.apache.camel.support.LoggerHelper;
@@ -63,7 +64,7 @@ import org.apache.camel.util.json.JsonObject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public final class DefaultBacklogDebugger extends ServiceSupport implements BacklogDebugger {
+public final class DefaultBacklogDebugger extends ServiceSupport implements BacklogDebugger, ShutdownPrepared {
 
     private static final Logger LOG = LoggerFactory.getLogger(DefaultBacklogDebugger.class);
 
@@ -267,6 +268,15 @@ public final class DefaultBacklogDebugger extends ServiceSupport implements Back
         }
     }
 
+    @Override
+    public void prepareShutdown(boolean suspendOnly, boolean forced) {
+        logger.log("Preparing debugger for shutdown");
+        // camel is being shutdown so if we are suspended during debugging
+        // then turn this off so messages can finish processing
+        suspendMode = false;
+        resumeMessageProcessing();
+    }
+
     /**
      * Resolves the value of the flag indicating whether the {@code BacklogDebugger} should suspend processing the
      * messages and wait for a debugger to attach or not.
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 a321429a24d7..73c7e1165023 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
@@ -2506,6 +2506,18 @@ public abstract class AbstractCamelContext extends BaseService
 
         forceLazyInitialization();
 
+        // setup cli-connector if not already done (before debugger)
+        if (hasService(CliConnector.class) == null) {
+            CliConnectorFactory ccf = getCamelContextExtension().getContextPlugin(CliConnectorFactory.class);
+            if (ccf != null && ccf.isEnabled()) {
+                CliConnector connector = ccf.createConnector();
+                addService(connector, true);
+                // force start cli connector early as otherwise it will be deferred until context is started
+                // but, we want status available during startup phase
+                ServiceHelper.startService(connector);
+            }
+        }
+
         // auto-detect camel-debug on classpath (if debugger has not been explicit added)
         boolean debuggerDetected = false;
         if (getDebugger() == null && hasService(BacklogDebugger.class) == null) {
@@ -2530,17 +2542,6 @@ public abstract class AbstractCamelContext extends BaseService
                 addService(backlog, true, true);
             }
         }
-        // setup cli-connector if not already done (after debugger)
-        if (hasService(CliConnector.class) == null) {
-            CliConnectorFactory ccf = getCamelContextExtension().getContextPlugin(CliConnectorFactory.class);
-            if (ccf != null && ccf.isEnabled()) {
-                CliConnector connector = ccf.createConnector();
-                addService(connector, true);
-                // force start cli connector early as otherwise it will be deferred until context is started
-                // but, we want status available during startup phase
-                ServiceHelper.startService(connector);
-            }
-        }
 
         addService(getManagementStrategy(), false);
 
diff --git a/docs/components/modules/others/nav.adoc b/docs/components/modules/others/nav.adoc
index 6ecb7d4919ce..dd4d8071bf88 100644
--- a/docs/components/modules/others/nav.adoc
+++ b/docs/components/modules/others/nav.adoc
@@ -6,6 +6,7 @@
 ** xref:aws-xray.adoc[AWS XRay]
 ** xref:azure-schema-registry.adoc[Azure Schema Registry]
 ** xref:cli-connector.adoc[CLI Connector]
+** xref:cli-debug.adoc[CLI Debug]
 ** xref:cloudevents.adoc[Cloudevents]
 ** xref:csimple-joor.adoc[CSimple jOOR]
 ** xref:cxf-transport.adoc[CXF Transport]
diff --git a/docs/components/modules/others/pages/cli-debug.adoc b/docs/components/modules/others/pages/cli-debug.adoc
new file mode 120000
index 000000000000..882fa9b85009
--- /dev/null
+++ b/docs/components/modules/others/pages/cli-debug.adoc
@@ -0,0 +1 @@
+../../../../../dsl/camel-cli-debug/src/main/docs/cli-debug.adoc
\ No newline at end of file
diff --git a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
index 0e43279af66d..6019e7d817f0 100644
--- a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
+++ b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
@@ -40,6 +40,7 @@ import java.util.Optional;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -63,6 +64,7 @@ import org.apache.camel.model.ProcessorDefinition;
 import org.apache.camel.model.ProcessorDefinitionHelper;
 import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.model.language.ExpressionDefinition;
+import org.apache.camel.spi.BacklogDebugger;
 import org.apache.camel.spi.CliConnector;
 import org.apache.camel.spi.CliConnectorFactory;
 import org.apache.camel.spi.ContextReloadStrategy;
@@ -72,6 +74,7 @@ import org.apache.camel.spi.Resource;
 import org.apache.camel.spi.ResourceLoader;
 import org.apache.camel.spi.ResourceReloadStrategy;
 import org.apache.camel.spi.RoutesLoader;
+import org.apache.camel.spi.ShutdownPrepared;
 import org.apache.camel.support.LoadOnDemandReloadStrategy;
 import org.apache.camel.support.MessageHelper;
 import org.apache.camel.support.PatternHelper;
@@ -101,6 +104,7 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C
 
     private final CliConnectorFactory cliConnectorFactory;
     private CamelContext camelContext;
+    private ScheduledFuture scheduledFuture;
     private int delay = 1000;
     private long counter;
     private String platform;
@@ -192,13 +196,32 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C
             messageHistoryFile = createLockFile(lockFile.getName() + "-history.json");
             debugFile = createLockFile(lockFile.getName() + "-debug.json");
             receiveFile = createLockFile(lockFile.getName() + "-receive.json");
-            executor.scheduleWithFixedDelay(this::task, 0, delay, TimeUnit.MILLISECONDS);
+            scheduledFuture = executor.scheduleWithFixedDelay(this::task, 0, delay, TimeUnit.MILLISECONDS);
             LOG.info("Camel JBang CLI enabled");
         } else {
             LOG.warn("Cannot create PID file: {}. This integration cannot be managed by Camel JBang CLI.", getPid());
         }
     }
 
+    @Override
+    public void updateDelay(int delay) {
+        if (this.delay == delay) {
+            return;
+        }
+        if (scheduledFuture != null) {
+            try {
+                scheduledFuture.cancel(true);
+            } catch (Exception e) {
+                // ignore
+            }
+        }
+        boolean done = scheduledFuture == null || scheduledFuture.isDone();
+        if (done) {
+            this.delay = delay;
+            scheduledFuture = executor.scheduleWithFixedDelay(this::task, 0, delay, TimeUnit.MILLISECONDS);
+        }
+    }
+
     @Override
     public void sigterm() {
         // we are terminating
@@ -214,6 +237,12 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C
             public void run() {
                 LOG.info("Camel JBang terminating JVM");
                 try {
+                    // if we are debugging then detach before stopping camel
+                    BacklogDebugger debugger = camelContext.hasService(BacklogDebugger.class);
+                    if (debugger instanceof ShutdownPrepared sp) {
+                        sp.prepareShutdown(false, true);
+                    }
+                    ServiceHelper.stopAndShutdownServices(debugger);
                     camelContext.stop();
                 } finally {
                     ServiceHelper.stopAndShutdownService(this);
@@ -300,6 +329,8 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C
                 doActionBrowseTask(root);
             } else if ("receive".equals(action)) {
                 doActionReceiveTask(root);
+            } else if ("cli-debug".equals(action)) {
+                doActionCliDebug(root);
             }
         } catch (Exception e) {
             // ignore
@@ -312,6 +343,23 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C
         }
     }
 
+    private void doActionCliDebug(JsonObject root) {
+        String command = root.getString("command");
+        String breakpoint = root.getString("breakpoint");
+        BacklogDebugger debugger = camelContext.hasService(BacklogDebugger.class);
+        if (debugger != null) {
+            if ("attach".equals(command)) {
+                if (breakpoint != null) {
+                    debugger.setInitialBreakpoints(breakpoint);
+                }
+                debugger.enableDebugger();
+                debugger.attach();
+            } else if ("detach".equals(command)) {
+                debugger.detach();
+            }
+        }
+    }
+
     private void doActionTransformTask(JsonObject root) throws Exception {
         StopWatch watch = new StopWatch();
         long timestamp = System.currentTimeMillis();
diff --git a/dsl/pom.xml b/dsl/camel-cli-debug/pom.xml
similarity index 64%
copy from dsl/pom.xml
copy to dsl/camel-cli-debug/pom.xml
index a3188d172db7..f4ae9f60b988 100644
--- a/dsl/pom.xml
+++ b/dsl/camel-cli-debug/pom.xml
@@ -17,51 +17,41 @@
     limitations under the License.
 
 -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
     <modelVersion>4.0.0</modelVersion>
 
     <parent>
         <groupId>org.apache.camel</groupId>
-        <artifactId>camel-parent</artifactId>
+        <artifactId>dsl</artifactId>
         <version>4.17.0-SNAPSHOT</version>
-        <relativePath>../parent/pom.xml</relativePath>
     </parent>
 
-    <artifactId>dsl</artifactId>
-    <packaging>pom</packaging>
-
-    <name>Camel :: DSL :: Parent</name>
-    <description>Camel DSL Parent</description>
+    <artifactId>camel-cli-debug</artifactId>
+    <packaging>jar</packaging>
+    <name>Camel :: DSL :: CLI Debug</name>
+    <description>Remote CLI debugger</description>
 
     <properties>
-        <camel.surefire.parallel>true</camel.surefire.parallel>
-        <camel-prepare-component>true</camel-prepare-component>
+        <firstVersion>4.17.0</firstVersion>
+        <title>CLI Debug</title>
+        <label>tooling</label>
     </properties>
 
-    <modules>
-        <module>camel-endpointdsl</module>
-        <module>camel-componentdsl</module>
-        <module>camel-cli-connector</module>
-        <module>camel-dsl-support</module>
-        <module>camel-dsl-modeline</module>
-        <module>camel-endpointdsl-support</module>
-        <module>camel-java-joor-dsl</module>
-        <module>camel-xml-io-dsl</module>
-        <module>camel-xml-jaxb-dsl</module>
-        <module>camel-xml-jaxb-dsl-test</module>
-        <module>camel-yaml-dsl</module>
-        <module>camel-kamelet-main</module>
-        <module>camel-jbang</module>
-    </modules>
+    <dependencies>
+
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-cli-connector</artifactId>
+        </dependency>
+
+    </dependencies>
 
     <build>
         <plugins>
             <plugin>
                 <groupId>org.apache.camel</groupId>
                 <artifactId>camel-package-maven-plugin</artifactId>
-                <configuration>
-                </configuration>
-
                 <executions>
                     <execution>
                         <id>generate</id>
@@ -79,24 +69,12 @@
                     </execution>
                 </executions>
             </plugin>
-            <plugin>
-                <artifactId>maven-compiler-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <id>recompile</id>
-                        <goals>
-                            <goal>compile</goal>
-                        </goals>
-                        <phase>process-classes</phase>
-                    </execution>
-                </executions>
-            </plugin>
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>build-helper-maven-plugin</artifactId>
                 <executions>
                     <execution>
-                        <phase>generate-sources</phase>
+                        <phase>initialize</phase>
                         <goals>
                             <goal>add-source</goal>
                             <goal>add-resource</goal>
@@ -116,4 +94,5 @@
             </plugin>
         </plugins>
     </build>
+
 </project>
diff --git a/dsl/camel-cli-debug/src/generated/resources/META-INF/services/org/apache/camel/debugger-factory b/dsl/camel-cli-debug/src/generated/resources/META-INF/services/org/apache/camel/debugger-factory
new file mode 100644
index 000000000000..1247dbb94f9a
--- /dev/null
+++ b/dsl/camel-cli-debug/src/generated/resources/META-INF/services/org/apache/camel/debugger-factory
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.cli.debug.CamelCliDebuggerFactory
diff --git a/dsl/camel-cli-debug/src/generated/resources/META-INF/services/org/apache/camel/other.properties b/dsl/camel-cli-debug/src/generated/resources/META-INF/services/org/apache/camel/other.properties
new file mode 100644
index 000000000000..d6d2632c5751
--- /dev/null
+++ b/dsl/camel-cli-debug/src/generated/resources/META-INF/services/org/apache/camel/other.properties
@@ -0,0 +1,7 @@
+# Generated by camel build tools - do NOT edit this file!
+name=cli-debug
+groupId=org.apache.camel
+artifactId=camel-cli-debug
+version=4.17.0-SNAPSHOT
+projectName=Camel :: DSL :: CLI Debug
+projectDescription=Remote CLI debugger
diff --git a/dsl/camel-cli-debug/src/generated/resources/cli-debug.json b/dsl/camel-cli-debug/src/generated/resources/cli-debug.json
new file mode 100644
index 000000000000..4acfc083d275
--- /dev/null
+++ b/dsl/camel-cli-debug/src/generated/resources/cli-debug.json
@@ -0,0 +1,15 @@
+{
+  "other": {
+    "kind": "other",
+    "name": "cli-debug",
+    "title": "CLI Debug",
+    "description": "Remote CLI debugger",
+    "deprecated": false,
+    "firstVersion": "4.17.0",
+    "label": "tooling",
+    "supportLevel": "Preview",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-cli-debug",
+    "version": "4.17.0-SNAPSHOT"
+  }
+}
diff --git a/dsl/camel-cli-debug/src/main/docs/cli-debug.adoc b/dsl/camel-cli-debug/src/main/docs/cli-debug.adoc
new file mode 100644
index 000000000000..265d41de125d
--- /dev/null
+++ b/dsl/camel-cli-debug/src/main/docs/cli-debug.adoc
@@ -0,0 +1,55 @@
+= CLI Debug Component
+:doctitle: CLI Debug
+:shortname: cli-debug
+:artifactid: camel-cli-debug
+:description: Remote CLI debugger
+:since: 4.17
+:supportlevel: Preview
+:tabs-sync-option:
+//Manually maintained attributes
+:camel-spring-boot-name: debug
+
+*Since Camel {since}*
+
+The camel-cli-debug enables Camel debugger for Camel CLI (camel-jbang).
+
+[IMPORTANT]
+====
+The camel-cli-debug is only for development purposes, it should **not** be used for production.
+
+Do not use both `camel-debug` and `camel-cli-debug` JARs in the Camel application classpath. Use only `camel-debug` JAR.
+====
+
+== Auto-detection from classpath
+
+To use this implementation all you need to do is to add the `camel-cli-debug` dependency to the classpath,
+and Camel should auto-detect this on startup and log as follows:
+
+[source,text]
+----
+Detected: camel-cli-debug JAR (Enabling Camel Debugging)
+----
+
+== Debugging
+
+Add `camel-cli-debug` JAR to the classpath of your application, and then run this application. For example with Spring Boot using `mvn spring-boot:run`.
+
+Then the application starts and Camel detects the CLI debugger that then logs a message, waiting for the CLI to remotely attach.
+
+From a CLI terminal then you can execute:
+
+[source,bash]
+----
+$ camel debug --remote-attach --name=<pid>
+----
+
+Where `<pid>` is the process id of the running Camel integration. You can see this from the log such as:
+
+[source,text]
+----
+2025-12-04T15:48:12.542+01:00  INFO 6667 --- [           main] o.a.c.impl.engine.AbstractCamelContext   : Detected: camel-cli-debug JAR (Enabling Camel Debugging)
+2025-12-04T15:48:12.543+01:00  INFO 6667 --- [           main] o.a.c.c.c.debug.CamelCliDebuggerFactory  : ================================================================================
+2025-12-04T15:48:12.543+01:00  INFO 6667 --- [           main] o.a.c.c.c.debug.CamelCliDebuggerFactory  : Waiting for CLI to remote attach (camel debug --remote-attach --name=6667)
+----
+
+To connect the CLI to the Camel application that will then continue to start and perform debugging from the CLI terminal.
\ No newline at end of file
diff --git a/dsl/camel-cli-debug/src/main/java/org/apache/camel/component/cli/debug/CamelCliDebuggerFactory.java b/dsl/camel-cli-debug/src/main/java/org/apache/camel/component/cli/debug/CamelCliDebuggerFactory.java
new file mode 100644
index 000000000000..4018beaf71cc
--- /dev/null
+++ b/dsl/camel-cli-debug/src/main/java/org/apache/camel/component/cli/debug/CamelCliDebuggerFactory.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.cli.debug;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.impl.debugger.DefaultBacklogDebugger;
+import org.apache.camel.spi.BacklogDebugger;
+import org.apache.camel.spi.CliConnector;
+import org.apache.camel.spi.Debugger;
+import org.apache.camel.spi.DebuggerFactory;
+import org.apache.camel.spi.annotations.JdkService;
+import org.apache.camel.support.LifecycleStrategySupport;
+import org.apache.camel.util.StopWatch;
+import org.apache.camel.util.concurrent.ThreadHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@JdkService(Debugger.FACTORY)
+public class CamelCliDebuggerFactory implements DebuggerFactory {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CamelCliDebuggerFactory.class);
+
+    private final AtomicBoolean hangupIntercepted = new AtomicBoolean();
+
+    @Override
+    // Debugger is created and added as a service. This method always returns a null object.
+    public Debugger createDebugger(CamelContext camelContext) throws Exception {
+        // only create a debugger if none already exists
+        if (camelContext.hasService(BacklogDebugger.class) == null) {
+
+            // NOTE: the AutoCloseable object is added as a Service, hence it is closed by Camel context
+            // according to the object lifecycle.
+            BacklogDebugger backlog = DefaultBacklogDebugger.createDebugger(camelContext); // NOSONAR
+            backlog.setStandby(true);
+            backlog.setLoggingLevel("DEBUG");
+            backlog.setSingleStepIncludeStartEnd(true);
+            backlog.setInitialBreakpoints(BacklogDebugger.BREAKPOINT_ALL_ROUTES);
+            backlog.setSuspendMode(true); // wait for attach via CLI
+
+            // must enable source location and history
+            // so debugger tooling knows to map breakpoints to source code
+            camelContext.setSourceLocationEnabled(true);
+            camelContext.setMessageHistory(true);
+
+            // enable debugger on camel
+            camelContext.setDebugging(true);
+
+            // we need to enable debugger after context is started
+            camelContext.addLifecycleStrategy(new LifecycleStrategySupport() {
+                @Override
+                public void onContextStarted(CamelContext context) {
+                    // noop
+                }
+
+                @Override
+                public void onContextStopping(CamelContext context) {
+                    backlog.detach();
+                    backlog.disableDebugger();
+                }
+            });
+            camelContext.addService(backlog, true, true);
+
+            // need to make debugger faster
+            CliConnector cli = camelContext.hasService(CliConnector.class);
+            if (cli != null) {
+                cli.updateDelay(100);
+            }
+
+            installHangupInterceptor();
+
+            long pid = ProcessHandle.current().pid();
+
+            LOG.info("=".repeat(80));
+            LOG.info("Waiting for CLI to remote attach (camel debug --remote-attach --name={})", pid);
+            StopWatch watch = new StopWatch();
+            while (!hangupIntercepted.get() && !backlog.isEnabled() && !camelContext.isStopping()) {
+                try {
+                    Thread.sleep(1000);
+                    if (watch.taken() > 5000) {
+                        LOG.info("Waiting for CLI to remote attach (camel debug --remote-attach --name={})", pid);
+                        watch.restart();
+                    }
+                } catch (InterruptedException e) {
+                    return null;
+                }
+            }
+            if (backlog.isEnabled()) {
+                LOG.info("CLI debugger attached");
+            }
+        }
+
+        // return null as we fool camel-core into using this backlog debugger as we added it as a service
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        return "camel-cli-debug";
+    }
+
+    private void handleHangup() {
+        hangupIntercepted.set(true);
+    }
+
+    private void installHangupInterceptor() {
+        Thread task = new Thread(this::handleHangup);
+        task.setName(ThreadHelper.resolveThreadName(null, "CamelCliDebuggerHangupInterceptor"));
+        Runtime.getRuntime().addShutdownHook(task);
+    }
+
+}
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Debug.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Debug.java
index c2bf8cbac795..8aac1e82db4c 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Debug.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Debug.java
@@ -42,8 +42,10 @@ import org.apache.camel.dsl.jbang.core.commands.action.MessageTableHelper;
 import org.apache.camel.dsl.jbang.core.common.CamelCommandHelper;
 import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
 import org.apache.camel.dsl.jbang.core.common.PathUtils;
+import org.apache.camel.dsl.jbang.core.common.ProcessHelper;
 import org.apache.camel.dsl.jbang.core.common.VersionHelper;
 import org.apache.camel.main.KameletMain;
+import org.apache.camel.support.PatternHelper;
 import org.apache.camel.util.FileUtil;
 import org.apache.camel.util.IOHelper;
 import org.apache.camel.util.StringHelper;
@@ -74,6 +76,10 @@ import static org.apache.camel.util.IOHelper.buffered;
 @Command(name = "debug", description = "Debug local Camel integration", sortOptions = false, showDefaultValues = true)
 public class Debug extends Run {
 
+    @CommandLine.Option(names = { "--remote-attach" },
+                        description = "Attaches debugger remotely to an existing running Camel integration. (Add camel-cli-debug JAR to the existing Camel application and run before attaching this debugger)")
+    boolean remoteAttach;
+
     @CommandLine.Option(names = { "--breakpoint" },
                         description = "To set breakpoint at the given node id (Multiple ids can be separated by comma). If no breakpoint is set, then the first route is automatic selected.")
     String breakpoint;
@@ -151,7 +157,12 @@ public class Debug extends Run {
             printConfigurationValues("Debugging integration with the following configuration:");
         }
 
-        Integer exit = runDebug();
+        Integer exit;
+        if (remoteAttach) {
+            exit = runRemoteAttach();
+        } else {
+            exit = runDebug();
+        }
         if (exit != null && exit != 0) {
             return exit;
         }
@@ -170,10 +181,12 @@ public class Debug extends Run {
         // read log input
         final AtomicBoolean quit = new AtomicBoolean();
         final Console c = System.console();
-        Thread t = new Thread(() -> {
-            doReadLog(quit);
-        }, "ReadLog");
-        t.start();
+        if (logLines > 0) {
+            Thread t = new Thread(() -> {
+                doReadLog(quit);
+            }, "ReadLog");
+            t.start();
+        }
 
         // read CLI input from user
         Thread t2 = new Thread(() -> doRead(c, quit), "ReadCommand");
@@ -205,6 +218,41 @@ public class Debug extends Run {
         return 0;
     }
 
+    private Integer runRemoteAttach() {
+        // find PID of running apps (if there are multiple then filter by name)
+        List<Long> pids = findPids(name);
+        if (pids.isEmpty()) {
+            return -1;
+        } else if (pids.size() > 1) {
+            printer().println("Name or pid " + name + " matches " + pids.size()
+                              + " running Camel integrations. Specify a name or PID that matches exactly one.");
+            return -1;
+        }
+        long pid = pids.get(0);
+
+        Path outputFile = getOutputFile(Long.toString(pid));
+        PathUtils.deleteFile(outputFile);
+
+        try {
+            JsonObject root = new JsonObject();
+            root.put("action", "cli-debug");
+            root.put("command", "attach");
+            if (breakpoint != null) {
+                root.put("breakpoint", breakpoint);
+            }
+            Path f = getActionFile(Long.toString(pid));
+            Files.writeString(f, root.toJson());
+        } catch (Exception e) {
+            return -1;
+        }
+
+        // attach to this pid
+        spawnPid = pid;
+        logLines = 0; // no logging possible
+
+        return 0;
+    }
+
     private void doReadLog(AtomicBoolean quit) {
         do {
             if (spawnOutput != null) {
@@ -1209,4 +1257,65 @@ public class Debug extends Run {
 
     }
 
+    List<Long> findPids(String name) {
+        List<Long> pids = new ArrayList<>();
+
+        // we need to know the pids of the running camel integrations
+        if (name.matches("\\d+")) {
+            return List.of(Long.parseLong(name));
+        } else {
+            if (name.endsWith("!")) {
+                // exclusive this name only
+                name = name.substring(0, name.length() - 1);
+            } else if (!name.endsWith("*")) {
+                // lets be open and match all that starts with this pattern
+                name = name + "*";
+            }
+        }
+
+        final long cur = ProcessHandle.current().pid();
+        final String pattern = name;
+        ProcessHandle.allProcesses()
+                .filter(ph -> ph.pid() != cur)
+                .forEach(ph -> {
+                    JsonObject root = loadStatus(ph.pid());
+                    // there must be a status file for the running Camel integration
+                    if (root != null) {
+                        String pName = ProcessHelper.extractName(root, ph);
+                        // ignore file extension, so it is easier to match by name
+                        pName = FileUtil.onlyName(pName);
+                        if (pName != null && !pName.isEmpty() && PatternHelper.matchPattern(pName, pattern)) {
+                            pids.add(ph.pid());
+                        } else {
+                            // try camel context name
+                            JsonObject context = (JsonObject) root.get("context");
+                            if (context != null) {
+                                pName = context.getString("name");
+                                if ("CamelJBang".equals(pName)) {
+                                    pName = null;
+                                }
+                                if (pName != null && !pName.isEmpty() && PatternHelper.matchPattern(pName, pattern)) {
+                                    pids.add(ph.pid());
+                                }
+                            }
+                        }
+                    }
+                });
+
+        return pids;
+    }
+
+    JsonObject loadStatus(long pid) {
+        try {
+            Path f = getStatusFile(Long.toString(pid));
+            if (f != null && Files.exists(f)) {
+                String text = Files.readString(f);
+                return (JsonObject) Jsoner.deserialize(text);
+            }
+        } catch (Exception e) {
+            // ignore
+        }
+        return null;
+    }
+
 }
diff --git a/dsl/pom.xml b/dsl/pom.xml
index a3188d172db7..2089278bba19 100644
--- a/dsl/pom.xml
+++ b/dsl/pom.xml
@@ -42,6 +42,7 @@
         <module>camel-endpointdsl</module>
         <module>camel-componentdsl</module>
         <module>camel-cli-connector</module>
+        <module>camel-cli-debug</module>
         <module>camel-dsl-support</module>
         <module>camel-dsl-modeline</module>
         <module>camel-endpointdsl-support</module>
diff --git a/parent/pom.xml b/parent/pom.xml
index e672c43b33e9..896e389ce8ba 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -2912,6 +2912,11 @@
                 <artifactId>camel-cli-connector</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.apache.camel</groupId>
+                <artifactId>camel-cli-debug</artifactId>
+                <version>${project.version}</version>
+            </dependency>
             <dependency>
                 <groupId>org.apache.camel</groupId>
                 <artifactId>camel-componentdsl</artifactId>