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 2022/01/23 11:42:20 UTC

[camel] branch main updated (ed4d79b -> 84a0574)

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

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


    from ed4d79b  CAMEL-17307: Add exception factory for predicate validation. Thanks to Tom Cassimon for the original work.
     new 2ecd092  CAMEL-17384: Developer Console SPI
     new 84a0574  Regen

The 2 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.


Summary of changes:
 apache-camel/pom.xml                               |  10 +-
 bom/camel-bom/pom.xml                              |  10 ++
 catalog/camel-catalog-console/pom.xml              | 135 +++++++++++++++
 .../services/org/apache/camel/dev-console/catalog  |   2 +
 .../camel/catalog/console/CatalogConsole.java      |  88 ++++++++++
 .../main/camel-main-configuration-metadata.json    |   1 +
 .../apache/camel/catalog/schemas/camel-spring.xsd  |  10 ++
 catalog/pom.xml                                    |   1 +
 .../apache/camel/spi/annotations/DevConsole.java}  |  28 ++--
 .../main/java/org/apache/camel/CamelContext.java   |  14 ++
 .../org/apache/camel/ExtendedCamelContext.java     |  15 ++
 .../java/org/apache/camel/console/DevConsole.java  |  78 +++++++++
 .../apache/camel/console/DevConsoleRegistry.java   | 113 +++++++++++++
 .../apache/camel/console/DevConsoleResolver.java}  |  23 ++-
 .../java/org/apache/camel/health/HealthCheck.java  |   2 +-
 .../camel/impl/engine/AbstractCamelContext.java    |  54 ++++++
 .../impl/engine/DefaultDevConsoleResolver.java     |  89 ++++++++++
 .../camel/impl/engine/SimpleCamelContext.java      |  18 ++
 .../impl/event/CamelContextResumeFailureEvent.java |   2 +-
 .../event/CamelContextStartupFailureEvent.java     |   2 +-
 .../impl/event/CamelContextStopFailureEvent.java   |   2 +-
 .../apache/camel/impl/event/RouteAddedEvent.java   |   2 +-
 .../camel/impl/event/RouteReloadedEvent.java       |   2 +-
 .../apache/camel/impl/event/RouteRemovedEvent.java |   2 +-
 .../apache/camel/impl/event/RouteStartedEvent.java |   2 +-
 .../camel/impl/event/RouteStartingEvent.java       |   2 +-
 .../apache/camel/impl/event/RouteStoppedEvent.java |   2 +-
 .../camel/impl/event/RouteStoppingEvent.java       |   2 +-
 .../impl/event/ServiceStartupFailureEvent.java     |   2 +-
 .../camel/impl/event/ServiceStopFailureEvent.java  |   2 +-
 .../camel/impl/event/StepCompletedEvent.java       |   2 +-
 .../apache/camel/impl/event/StepFailedEvent.java   |   2 +-
 .../apache/camel/impl/event/StepStartedEvent.java  |   2 +-
 core/camel-console/pom.xml                         |  80 +++++++++
 .../camel/impl/console/EventConsoleConfigurer.java |  55 ++++++
 .../impl/console/JvmDevConsoleConfigurer.java      |  55 ++++++
 .../impl/console/MemoryDevConsoleConfigurer.java   |  49 ++++++
 .../org.apache.camel.impl.console.EventConsole     |   2 +
 .../org.apache.camel.impl.console.JvmDevConsole    |   2 +
 .../org.apache.camel.impl.console.MemoryDevConsole |   2 +
 .../services/org/apache/camel/dev-console/context  |   2 +
 .../org/apache/camel/dev-console/default-registry  |   2 +
 .../services/org/apache/camel/dev-console/event    |   2 +
 .../services/org/apache/camel/dev-console/health   |   2 +
 .../services/org/apache/camel/dev-console/jvm      |   2 +
 .../services/org/apache/camel/dev-console/memory   |   2 +
 .../org/apache/camel/dev-console/properties        |   2 +
 .../camel/impl/console/AbstractDevConsole.java     | 114 +++++++++++++
 .../camel/impl/console/ContextDevConsole.java      |  53 ++++++
 .../impl/console/DefaultDevConsoleRegistry.java    | 185 +++++++++++++++++++++
 .../impl/console/DefaultDevConsolesLoader.java}    |  54 +++---
 .../apache/camel/impl/console/EventConsole.java    | 111 +++++++++++++
 .../camel/impl/console/HealthDevConsole.java       |  65 ++++++++
 .../apache/camel/impl/console/JvmDevConsole.java   |  76 +++++++++
 .../camel/impl/console/MemoryDevConsole.java       |  58 +++++++
 .../camel/impl/console/PropertiesDevConsole.java   |  49 ++++++
 .../camel/impl/console/ContextDevConsoleTest.java  |  39 +++++
 .../console/DefaultDevConsolesLoaderTest.java}     |  23 +--
 .../src/test/resources/log4j2.properties           |  33 ++++
 .../camel/impl/ExtendedCamelContextConfigurer.java |  12 ++
 .../camel/impl/lw/LightweightCamelContext.java     |  21 +++
 .../impl/lw/LightweightRuntimeCamelContext.java    |  23 +++
 .../core/xml/AbstractCamelContextFactoryBean.java  |  21 +++
 ...ventNotifierServiceStoppingFailedEventTest.java |   2 +-
 .../camel/impl/health/ContextHealthCheck.java      |  23 +--
 .../impl/health/DefaultHealthChecksLoader.java     |   2 +-
 .../MainConfigurationPropertiesConfigurer.java     |   6 +
 .../camel-main-configuration-metadata.json         |   1 +
 core/camel-main/src/main/docs/main.adoc            |   3 +-
 .../org/apache/camel/main/BaseMainSupport.java     |  43 ++++-
 .../camel/main/DefaultConfigurationConfigurer.java |  22 +++
 .../camel/main/DefaultConfigurationProperties.java |  24 +++
 .../java/org/apache/camel/support/EventHelper.java |   2 +-
 .../main/java/org/apache/camel/util/UnitUtils.java |  45 +++++
 .../java/org/apache/camel/util/UnitUtilsTest.java  |  19 ++-
 core/pom.xml                                       |   1 +
 docs/user-manual/modules/ROOT/nav.adoc             |   1 +
 .../modules/ROOT/pages/camel-console.adoc          | 142 ++++++++++++++++
 .../apache/camel/dsl/jbang/core/commands/Run.java  |   7 +-
 dsl/camel-kamelet-main/pom.xml                     |  20 +++
 .../java/org/apache/camel/main/KameletMain.java    |  11 ++
 .../org/apache/camel/main/VertxHttpServer.java     |  89 +++++++---
 parent/pom.xml                                     |  10 ++
 .../apache/camel/spi/annotations/DevConsole.java   |  28 ++--
 84 files changed, 2273 insertions(+), 145 deletions(-)
 create mode 100644 catalog/camel-catalog-console/pom.xml
 create mode 100644 catalog/camel-catalog-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/catalog
 create mode 100644 catalog/camel-catalog-console/src/main/java/org/apache/camel/catalog/console/CatalogConsole.java
 copy core/{camel-base/src/main/java/org/apache/camel/impl/event/StepCompletedEvent.java => camel-api/src/generated/java/org/apache/camel/spi/annotations/DevConsole.java} (61%)
 create mode 100644 core/camel-api/src/main/java/org/apache/camel/console/DevConsole.java
 create mode 100644 core/camel-api/src/main/java/org/apache/camel/console/DevConsoleRegistry.java
 copy core/{camel-base/src/main/java/org/apache/camel/impl/event/StepStartedEvent.java => camel-api/src/main/java/org/apache/camel/console/DevConsoleResolver.java} (63%)
 create mode 100644 core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDevConsoleResolver.java
 create mode 100644 core/camel-console/pom.xml
 create mode 100644 core/camel-console/src/generated/java/org/apache/camel/impl/console/EventConsoleConfigurer.java
 create mode 100644 core/camel-console/src/generated/java/org/apache/camel/impl/console/JvmDevConsoleConfigurer.java
 create mode 100644 core/camel-console/src/generated/java/org/apache/camel/impl/console/MemoryDevConsoleConfigurer.java
 create mode 100644 core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.EventConsole
 create mode 100644 core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.JvmDevConsole
 create mode 100644 core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.MemoryDevConsole
 create mode 100644 core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/context
 create mode 100644 core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/default-registry
 create mode 100644 core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/event
 create mode 100644 core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/health
 create mode 100644 core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/jvm
 create mode 100644 core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/memory
 create mode 100644 core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/properties
 create mode 100644 core/camel-console/src/main/java/org/apache/camel/impl/console/AbstractDevConsole.java
 create mode 100644 core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
 create mode 100644 core/camel-console/src/main/java/org/apache/camel/impl/console/DefaultDevConsoleRegistry.java
 copy core/{camel-health/src/main/java/org/apache/camel/impl/health/DefaultHealthChecksLoader.java => camel-console/src/main/java/org/apache/camel/impl/console/DefaultDevConsolesLoader.java} (59%)
 create mode 100644 core/camel-console/src/main/java/org/apache/camel/impl/console/EventConsole.java
 create mode 100644 core/camel-console/src/main/java/org/apache/camel/impl/console/HealthDevConsole.java
 create mode 100644 core/camel-console/src/main/java/org/apache/camel/impl/console/JvmDevConsole.java
 create mode 100644 core/camel-console/src/main/java/org/apache/camel/impl/console/MemoryDevConsole.java
 create mode 100644 core/camel-console/src/main/java/org/apache/camel/impl/console/PropertiesDevConsole.java
 create mode 100644 core/camel-console/src/test/java/org/apache/camel/impl/console/ContextDevConsoleTest.java
 copy core/{camel-base/src/main/java/org/apache/camel/impl/event/RouteStartedEvent.java => camel-console/src/test/java/org/apache/camel/impl/console/DefaultDevConsolesLoaderTest.java} (59%)
 create mode 100644 core/camel-console/src/test/resources/log4j2.properties
 create mode 100644 docs/user-manual/modules/ROOT/pages/camel-console.adoc
 copy core/camel-base/src/main/java/org/apache/camel/impl/event/StepCompletedEvent.java => tooling/spi-annotations/src/main/java/org/apache/camel/spi/annotations/DevConsole.java (61%)

[camel] 02/02: Regen

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

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

commit 84a057436d39b7ea4cad43d9cd966e8d8de08d23
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sun Jan 23 12:17:53 2022 +0100

    Regen
---
 .../org/apache/camel/catalog/schemas/camel-spring.xsd          | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
index dfb17ca..631ce4f 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
@@ -12238,6 +12238,16 @@ Sets a reference to use for lookup the policy in the registry.
           <xs:element ref="tns:xpath"/>
           <xs:element ref="tns:xquery"/>
         </xs:choice>
+        <xs:attribute name="predicateExceptionFactory" type="xs:string">
+          <xs:annotation>
+            <xs:documentation xml:lang="en"><![CDATA[
+The bean id of custom PredicateExceptionFactory to use for creating the
+exception when the validation fails. By default, Camel will throw
+PredicateValidationException. By using a custom factory you can control which
+exception to throw instead.
+            ]]></xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
       </xs:extension>
     </xs:complexContent>
   </xs:complexType>

[camel] 01/02: CAMEL-17384: Developer Console SPI

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

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

commit 2ecd092140644aadd854e5063354a906f66e88aa
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sun Jan 23 12:17:36 2022 +0100

    CAMEL-17384: Developer Console SPI
---
 apache-camel/pom.xml                               |  10 +-
 bom/camel-bom/pom.xml                              |  10 ++
 catalog/camel-catalog-console/pom.xml              | 135 +++++++++++++++
 .../services/org/apache/camel/dev-console/catalog  |   2 +
 .../camel/catalog/console/CatalogConsole.java      |  88 ++++++++++
 .../main/camel-main-configuration-metadata.json    |   1 +
 catalog/pom.xml                                    |   1 +
 .../apache/camel/spi/annotations/DevConsole.java}  |  28 ++--
 .../main/java/org/apache/camel/CamelContext.java   |  14 ++
 .../org/apache/camel/ExtendedCamelContext.java     |  15 ++
 .../java/org/apache/camel/console/DevConsole.java  |  78 +++++++++
 .../apache/camel/console/DevConsoleRegistry.java   | 113 +++++++++++++
 .../apache/camel/console/DevConsoleResolver.java}  |  23 ++-
 .../java/org/apache/camel/health/HealthCheck.java  |   2 +-
 .../camel/impl/engine/AbstractCamelContext.java    |  54 ++++++
 .../impl/engine/DefaultDevConsoleResolver.java     |  89 ++++++++++
 .../camel/impl/engine/SimpleCamelContext.java      |  18 ++
 .../impl/event/CamelContextResumeFailureEvent.java |   2 +-
 .../event/CamelContextStartupFailureEvent.java     |   2 +-
 .../impl/event/CamelContextStopFailureEvent.java   |   2 +-
 .../apache/camel/impl/event/RouteAddedEvent.java   |   2 +-
 .../camel/impl/event/RouteReloadedEvent.java       |   2 +-
 .../apache/camel/impl/event/RouteRemovedEvent.java |   2 +-
 .../apache/camel/impl/event/RouteStartedEvent.java |   2 +-
 .../camel/impl/event/RouteStartingEvent.java       |   2 +-
 .../apache/camel/impl/event/RouteStoppedEvent.java |   2 +-
 .../camel/impl/event/RouteStoppingEvent.java       |   2 +-
 .../impl/event/ServiceStartupFailureEvent.java     |   2 +-
 .../camel/impl/event/ServiceStopFailureEvent.java  |   2 +-
 .../camel/impl/event/StepCompletedEvent.java       |   2 +-
 .../apache/camel/impl/event/StepFailedEvent.java   |   2 +-
 .../apache/camel/impl/event/StepStartedEvent.java  |   2 +-
 core/camel-console/pom.xml                         |  80 +++++++++
 .../camel/impl/console/EventConsoleConfigurer.java |  55 ++++++
 .../impl/console/JvmDevConsoleConfigurer.java      |  55 ++++++
 .../impl/console/MemoryDevConsoleConfigurer.java   |  49 ++++++
 .../org.apache.camel.impl.console.EventConsole     |   2 +
 .../org.apache.camel.impl.console.JvmDevConsole    |   2 +
 .../org.apache.camel.impl.console.MemoryDevConsole |   2 +
 .../services/org/apache/camel/dev-console/context  |   2 +
 .../org/apache/camel/dev-console/default-registry  |   2 +
 .../services/org/apache/camel/dev-console/event    |   2 +
 .../services/org/apache/camel/dev-console/health   |   2 +
 .../services/org/apache/camel/dev-console/jvm      |   2 +
 .../services/org/apache/camel/dev-console/memory   |   2 +
 .../org/apache/camel/dev-console/properties        |   2 +
 .../camel/impl/console/AbstractDevConsole.java     | 114 +++++++++++++
 .../camel/impl/console/ContextDevConsole.java      |  53 ++++++
 .../impl/console/DefaultDevConsoleRegistry.java    | 185 +++++++++++++++++++++
 .../impl/console/DefaultDevConsolesLoader.java}    |  54 +++---
 .../apache/camel/impl/console/EventConsole.java    | 111 +++++++++++++
 .../camel/impl/console/HealthDevConsole.java       |  65 ++++++++
 .../apache/camel/impl/console/JvmDevConsole.java   |  76 +++++++++
 .../camel/impl/console/MemoryDevConsole.java       |  58 +++++++
 .../camel/impl/console/PropertiesDevConsole.java   |  49 ++++++
 .../camel/impl/console/ContextDevConsoleTest.java  |  39 +++++
 .../console/DefaultDevConsolesLoaderTest.java}     |  23 +--
 .../src/test/resources/log4j2.properties           |  33 ++++
 .../camel/impl/ExtendedCamelContextConfigurer.java |  12 ++
 .../camel/impl/lw/LightweightCamelContext.java     |  21 +++
 .../impl/lw/LightweightRuntimeCamelContext.java    |  23 +++
 .../core/xml/AbstractCamelContextFactoryBean.java  |  21 +++
 ...ventNotifierServiceStoppingFailedEventTest.java |   2 +-
 .../camel/impl/health/ContextHealthCheck.java      |  23 +--
 .../impl/health/DefaultHealthChecksLoader.java     |   2 +-
 .../MainConfigurationPropertiesConfigurer.java     |   6 +
 .../camel-main-configuration-metadata.json         |   1 +
 core/camel-main/src/main/docs/main.adoc            |   3 +-
 .../org/apache/camel/main/BaseMainSupport.java     |  43 ++++-
 .../camel/main/DefaultConfigurationConfigurer.java |  22 +++
 .../camel/main/DefaultConfigurationProperties.java |  24 +++
 .../java/org/apache/camel/support/EventHelper.java |   2 +-
 .../main/java/org/apache/camel/util/UnitUtils.java |  45 +++++
 .../java/org/apache/camel/util/UnitUtilsTest.java  |  19 ++-
 core/pom.xml                                       |   1 +
 docs/user-manual/modules/ROOT/nav.adoc             |   1 +
 .../modules/ROOT/pages/camel-console.adoc          | 142 ++++++++++++++++
 .../apache/camel/dsl/jbang/core/commands/Run.java  |   7 +-
 dsl/camel-kamelet-main/pom.xml                     |  20 +++
 .../java/org/apache/camel/main/KameletMain.java    |  11 ++
 .../org/apache/camel/main/VertxHttpServer.java     |  89 +++++++---
 parent/pom.xml                                     |  10 ++
 .../apache/camel/spi/annotations/DevConsole.java   |  28 ++--
 83 files changed, 2263 insertions(+), 145 deletions(-)

diff --git a/apache-camel/pom.xml b/apache-camel/pom.xml
index 9aec058..dba6eac 100644
--- a/apache-camel/pom.xml
+++ b/apache-camel/pom.xml
@@ -114,6 +114,10 @@
         </dependency>
         <dependency>
             <groupId>org.apache.camel</groupId>
+            <artifactId>camel-console</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
             <artifactId>camel-health</artifactId>
         </dependency>
         <dependency>
@@ -160,13 +164,17 @@
             <type>pom</type>
         </dependency>
 
-        <!-- camel catalog and commands -->
+        <!-- camel catalog -->
         <dependency>
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-catalog</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.camel</groupId>
+            <artifactId>camel-catalog-console</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
             <artifactId>camel-catalog-lucene</artifactId>
         </dependency>
         <dependency>
diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml
index b62899e..d220364 100644
--- a/bom/camel-bom/pom.xml
+++ b/bom/camel-bom/pom.xml
@@ -398,6 +398,11 @@
       </dependency>
       <dependency>
         <groupId>org.apache.camel</groupId>
+        <artifactId>camel-catalog-console</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
         <artifactId>camel-catalog-lucene</artifactId>
         <version>${project.version}</version>
       </dependency>
@@ -478,6 +483,11 @@
       </dependency>
       <dependency>
         <groupId>org.apache.camel</groupId>
+        <artifactId>camel-console</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
         <artifactId>camel-consul</artifactId>
         <version>${project.version}</version>
       </dependency>
diff --git a/catalog/camel-catalog-console/pom.xml b/catalog/camel-catalog-console/pom.xml
new file mode 100644
index 0000000..bef95f0
--- /dev/null
+++ b/catalog/camel-catalog-console/pom.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<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>catalog</artifactId>
+        <version>3.15.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>camel-catalog-console</artifactId>
+    <packaging>jar</packaging>
+    <name>Camel :: Catalog :: Console</name>
+    <description>Camel Catalog Developer Console</description>
+
+    <properties>
+        <firstVersion>3.15.0</firstVersion>
+        <label>tooling</label>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-console</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-catalog</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <!-- disable all components -->
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-allcomponents</artifactId>
+            <version>${project.version}</version>
+            <type>pom</type>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.camel</groupId>
+                    <artifactId>*</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- logging -->
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-slf4j-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+
+            <plugin>
+                <groupId>org.apache.camel</groupId>
+                <artifactId>camel-bundle-plugin</artifactId>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.camel</groupId>
+                <artifactId>camel-package-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>generate</id>
+                        <goals>
+                            <goal>generate</goal>
+                        </goals>
+                        <phase>process-classes</phase>
+                    </execution>
+                    <execution>
+                        <id>generate-postcompile</id>
+                        <goals>
+                            <goal>generate-postcompile</goal>
+                        </goals>
+                        <phase>prepare-package</phase>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>add-source</goal>
+                            <goal>add-resource</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>src/generated/java</source>
+                            </sources>
+                            <resources>
+                                <resource>
+                                    <directory>src/generated/resources</directory>
+                                </resource>
+                            </resources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/catalog/camel-catalog-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/catalog b/catalog/camel-catalog-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/catalog
new file mode 100644
index 0000000..03d5cbd
--- /dev/null
+++ b/catalog/camel-catalog-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/catalog
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.catalog.console.CatalogConsole
diff --git a/catalog/camel-catalog-console/src/main/java/org/apache/camel/catalog/console/CatalogConsole.java b/catalog/camel-catalog-console/src/main/java/org/apache/camel/catalog/console/CatalogConsole.java
new file mode 100644
index 0000000..068717c
--- /dev/null
+++ b/catalog/camel-catalog-console/src/main/java/org/apache/camel/catalog/console/CatalogConsole.java
@@ -0,0 +1,88 @@
+/*
+ * 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.catalog.console;
+
+import java.util.Map;
+
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.impl.console.AbstractDevConsole;
+import org.apache.camel.spi.annotations.DevConsole;
+import org.apache.camel.tooling.model.ArtifactModel;
+import org.apache.camel.tooling.model.OtherModel;
+
+@DevConsole("catalog")
+public class CatalogConsole extends AbstractDevConsole {
+
+    private static final String CP = System.getProperty("java.class.path");
+    private final CamelCatalog catalog = new DefaultCamelCatalog(true);
+
+    public CatalogConsole() {
+        super("camel", "catalog", "Catalog", "Lists all the used Camel Components");
+    }
+
+    @Override
+    protected Object doCall(MediaType mediaType, Map<String, Object> options) {
+        // only text is supported
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("\nComponents:\n");
+        getCamelContext().getComponentNames().forEach(n -> appendModel(catalog.componentModel(n), sb));
+        sb.append("\n\nLanguages:\n");
+        getCamelContext().getLanguageNames().forEach(n -> appendModel(catalog.languageModel(n), sb));
+        sb.append("\n\nData Formats:\n");
+        getCamelContext().getDataFormatNames().forEach(n -> appendModel(catalog.dataFormatModel(n), sb));
+
+        // misc is harder to find as we need to find them via classpath
+        sb.append("\n\nMiscellaneous Components:\n");
+        String[] cp = CP.split("[:|;]");
+        String suffix = "-" + getCamelContext().getVersion() + ".jar";
+        for (String c : cp) {
+            if (c.endsWith(suffix)) {
+                int pos = Math.max(c.lastIndexOf("/"), c.lastIndexOf("\\"));
+                if (pos > 0) {
+                    c = c.substring(pos + 1, c.length() - suffix.length());
+                    appendModel(findOtherModel(c), sb);
+                }
+            }
+        }
+
+        return sb.toString();
+    }
+
+    private ArtifactModel findOtherModel(String artifactId) {
+        // is it a mist component
+        for (String name : catalog.findOtherNames()) {
+            OtherModel model = catalog.otherModel(name);
+            if (model != null && model.getArtifactId().equals(artifactId)) {
+                return model;
+            }
+        }
+        return null;
+    }
+
+    private static void appendModel(ArtifactModel<?> model, StringBuilder sb) {
+        if (model != null) {
+            String level = model.getSupportLevel().toString();
+            if (model.isDeprecated()) {
+                level += "-deprecated";
+            }
+            sb.append(String.format("\n    %s %s %s %s %s", model.getTitle(), model.getArtifactId(), level,
+                    model.getFirstVersionShort(), model.getDescription()));
+        }
+    }
+}
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
index ea0b01f..f68a484 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
@@ -27,6 +27,7 @@
     { "name": "camel.main.configurations", "description": "Sets the configuration objects used to configure the camel context.", "sourceType": "org.apache.camel.main.MainConfigurationProperties", "type": "object", "javaType": "java.util.List" },
     { "name": "camel.main.consumerTemplateCacheSize", "description": "Consumer template endpoints cache size.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "integer", "javaType": "int", "defaultValue": 1000 },
     { "name": "camel.main.debugging", "description": "Sets whether debugging is enabled or not. Default is false.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" },
+    { "name": "camel.main.devConsoleEnabled", "description": "Whether to enable developer console (requires camel-console on classpath). The developer console is only for assisting during development. This is NOT for production usage.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" },
     { "name": "camel.main.dumpRoutes", "description": "If dumping is enabled then Camel will during startup dump all loaded routes (incl rests and route templates) represented as XML DSL into the log. This is intended for trouble shooting or to assist during development. Sensitive information that may be configured in the route endpoints could potentially be included in the dump output and is therefore not recommended to be used for production usage. This requires to have camel-xml-jaxb  [...]
     { "name": "camel.main.durationHitExitCode", "description": "Sets the exit code for the application if duration was hit", "sourceType": "org.apache.camel.main.MainConfigurationProperties", "type": "integer", "javaType": "int" },
     { "name": "camel.main.durationMaxAction", "description": "Controls whether the Camel application should shutdown the JVM, or stop all routes, when duration max is triggered.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "string", "javaType": "java.lang.String", "defaultValue": "shutdown", "enum": [ "shutdown", "stop" ] },
diff --git a/catalog/pom.xml b/catalog/pom.xml
index 8bb0e27..2b68c02 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -40,6 +40,7 @@
         <module>dummy-component</module>
         <!-- catalog -->
         <module>camel-catalog</module>
+        <module>camel-catalog-console</module>
         <module>camel-catalog-lucene</module>
         <module>camel-catalog-maven</module>
         <!-- tooling that depends on catalog -->
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/StepCompletedEvent.java b/core/camel-api/src/generated/java/org/apache/camel/spi/annotations/DevConsole.java
similarity index 61%
copy from core/camel-base/src/main/java/org/apache/camel/impl/event/StepCompletedEvent.java
copy to core/camel-api/src/generated/java/org/apache/camel/spi/annotations/DevConsole.java
index d90b195..f504ea0 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/StepCompletedEvent.java
+++ b/core/camel-api/src/generated/java/org/apache/camel/spi/annotations/DevConsole.java
@@ -14,22 +14,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.impl.event;
+package org.apache.camel.spi.annotations;
 
-import org.apache.camel.Exchange;
-import org.apache.camel.spi.CamelEvent;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
 /**
- * Event after a step has been completed successfully.
+ * Marks a class as a custom developer console.
  */
-public class StepCompletedEvent extends AbstractStepEvent implements CamelEvent.StepCompletedEvent {
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Target({ ElementType.TYPE })
+@ServiceFactory("dev-console")
+public @interface DevConsole {
 
-    public StepCompletedEvent(Exchange source, String stepId) {
-        super(source, stepId);
-    }
+    /**
+     * The ID of the dev console.
+     */
+    String value();
 
-    @Override
-    public String toString() {
-        return "Step[" + getStepId() + "] completed";
-    }
 }
diff --git a/core/camel-api/src/main/java/org/apache/camel/CamelContext.java b/core/camel-api/src/main/java/org/apache/camel/CamelContext.java
index 08abbe7..1ee5bd2 100644
--- a/core/camel-api/src/main/java/org/apache/camel/CamelContext.java
+++ b/core/camel-api/src/main/java/org/apache/camel/CamelContext.java
@@ -1204,6 +1204,20 @@ public interface CamelContext extends CamelContextLifecycle, RuntimeConfiguratio
     void setSourceLocationEnabled(Boolean sourceLocationEnabled);
 
     /**
+     * Whether to enable developer console (requires camel-console on classpath).
+     *
+     * The developer console is only for assisting during development. This is NOT for production usage.
+     */
+    Boolean isDevConsole();
+
+    /**
+     * Whether to enable developer console (requires camel-console on classpath)
+     *
+     * The developer console is only for assisting during development. This is NOT for production usage.
+     */
+    void setDevConsole(Boolean loadDevConsoles);
+
+    /**
      * Whether or not type converter statistics is enabled.
      * <p/>
      * By default the type converter utilization statistics is disabled. <b>Notice:</b> If enabled then there is a
diff --git a/core/camel-api/src/main/java/org/apache/camel/ExtendedCamelContext.java b/core/camel-api/src/main/java/org/apache/camel/ExtendedCamelContext.java
index 4f66619..764ec0e 100644
--- a/core/camel-api/src/main/java/org/apache/camel/ExtendedCamelContext.java
+++ b/core/camel-api/src/main/java/org/apache/camel/ExtendedCamelContext.java
@@ -22,6 +22,7 @@ import java.util.Set;
 import java.util.concurrent.ScheduledExecutorService;
 
 import org.apache.camel.catalog.RuntimeCamelCatalog;
+import org.apache.camel.console.DevConsoleResolver;
 import org.apache.camel.health.HealthCheckResolver;
 import org.apache.camel.spi.AnnotationBasedProcessorFactory;
 import org.apache.camel.spi.AsyncProcessorAwaitManager;
@@ -355,6 +356,20 @@ public interface ExtendedCamelContext extends CamelContext {
     void setHealthCheckResolver(HealthCheckResolver healthCheckResolver);
 
     /**
+     * Gets the current dev console resolver
+     *
+     * @return the resolver
+     */
+    DevConsoleResolver getDevConsoleResolver();
+
+    /**
+     * Sets a custom dev console resolver
+     *
+     * @param devConsoleResolver the resolver
+     */
+    void setDevConsoleResolver(DevConsoleResolver devConsoleResolver);
+
+    /**
      * Returns the package scanning class resolver
      *
      * @return the resolver
diff --git a/core/camel-api/src/main/java/org/apache/camel/console/DevConsole.java b/core/camel-api/src/main/java/org/apache/camel/console/DevConsole.java
new file mode 100644
index 0000000..518fbb2
--- /dev/null
+++ b/core/camel-api/src/main/java/org/apache/camel/console/DevConsole.java
@@ -0,0 +1,78 @@
+/*
+ * 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.console;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Developer Console
+ */
+public interface DevConsole {
+
+    String CONSOLE_ID = "console.id";
+    String CONSOLE_GROUP = "console.group";
+
+    enum MediaType {
+        TEXT,
+        JSON
+    }
+
+    /**
+     * The group of this console.
+     */
+    String getGroup();
+
+    /**
+     * The ID of this console.
+     */
+    String getId();
+
+    /**
+     * Display name of this console.
+     */
+    String getDisplayName();
+
+    /**
+     * Short description of this console.
+     */
+    String getDescription();
+
+    /**
+     * Whether this console supports the given media type.
+     *
+     * @param  mediaType the media type
+     * @return           true if support, false if not
+     */
+    boolean supportMediaType(MediaType mediaType);
+
+    /**
+     * Invokes and gets the output from this console.
+     */
+    default Object call(MediaType mediaType) {
+        return call(mediaType, Collections.emptyMap());
+    }
+
+    /**
+     * Invokes and gets the output from this console.
+     *
+     * The options argument can be used to pass information specific to the call. The implementation is responsible to
+     * catch and handle any exception thrown by the underlying technology, including unchecked ones.
+     */
+    Object call(MediaType mediaType, Map<String, Object> options);
+
+}
diff --git a/core/camel-api/src/main/java/org/apache/camel/console/DevConsoleRegistry.java b/core/camel-api/src/main/java/org/apache/camel/console/DevConsoleRegistry.java
new file mode 100644
index 0000000..f3051f5
--- /dev/null
+++ b/core/camel-api/src/main/java/org/apache/camel/console/DevConsoleRegistry.java
@@ -0,0 +1,113 @@
+/*
+ * 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.console;
+
+import java.util.Collection;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.StaticService;
+import org.apache.camel.spi.IdAware;
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * A registry for dev console.
+ */
+public interface DevConsoleRegistry extends CamelContextAware, StaticService, IdAware {
+
+    /**
+     * Service factory name.
+     */
+    String NAME = "default-registry";
+
+    /**
+     * Service factory key.
+     */
+    String FACTORY = "dev-console/" + NAME;
+
+    /**
+     * Whether dev console is enabled globally
+     */
+    boolean isEnabled();
+
+    /**
+     * Whether dev console is enabled globally
+     */
+    void setEnabled(boolean enabled);
+
+    /**
+     * Resolves {@link DevConsole by id.
+     *
+     * Will first lookup in this {@link DevConsoleRegistry} and then {@link org.apache.camel.spi.Registry}, and lastly
+     * do classpath scanning via {@link org.apache.camel.spi.annotations.ServiceFactory}.
+     *
+     * @return either {@link DevConsole}, or <tt>null</tt> if none found.
+     */
+    DevConsole resolveById(String id);
+
+    /**
+     * Registers a {@link DevConsole}.
+     */
+    boolean register(DevConsole console);
+
+    /**
+     * Unregisters a {@link DevConsole}.
+     */
+    boolean unregister(DevConsole console);
+
+    /**
+     * A collection of dev console IDs.
+     */
+    default Collection<String> getConsoleIDs() {
+        return stream()
+                .map(DevConsole::getId)
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Returns the dev console identified by the given <code>id</code> if available.
+     */
+    default Optional<DevConsole> getConsole(String id) {
+        return stream()
+                .filter(r -> ObjectHelper.equal(r.getId(), id))
+                .findFirst();
+    }
+
+    /**
+     * Returns an optional {@link DevConsoleRegistry}, by default no registry is present, and it must be explicit
+     * activated. Components can register/unregister dev consoles in response to life-cycle events (i.e. start/stop).
+     *
+     * This registry is not used by the camel context, but it is up to the implementation to properly use it.
+     */
+    static DevConsoleRegistry get(CamelContext context) {
+        return context.getExtension(DevConsoleRegistry.class);
+    }
+
+    /**
+     * Returns a sequential {@code Stream} with the known {@link DevConsole} as its source.
+     */
+    Stream<DevConsole> stream();
+
+    /**
+     * Loads custom dev consoles by scanning classpath.
+     */
+    void loadDevConsoles();
+
+}
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/StepStartedEvent.java b/core/camel-api/src/main/java/org/apache/camel/console/DevConsoleResolver.java
similarity index 63%
copy from core/camel-base/src/main/java/org/apache/camel/impl/event/StepStartedEvent.java
copy to core/camel-api/src/main/java/org/apache/camel/console/DevConsoleResolver.java
index 8c3c645..bae9ca0 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/StepStartedEvent.java
+++ b/core/camel-api/src/main/java/org/apache/camel/console/DevConsoleResolver.java
@@ -14,22 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.impl.event;
+package org.apache.camel.console;
 
-import org.apache.camel.Exchange;
-import org.apache.camel.spi.CamelEvent;
+import org.apache.camel.CamelContextAware;
 
 /**
- * Event after a step has been created.
+ * A pluggable strategy for resolving dev consoles in a loosely coupled manner
  */
-public class StepStartedEvent extends AbstractStepEvent implements CamelEvent.StepStartedEvent {
+public interface DevConsoleResolver extends CamelContextAware {
 
-    public StepStartedEvent(Exchange source, String stepId) {
-        super(source, stepId);
-    }
+    /**
+     * Resolves the given {@link DevConsole}.
+     *
+     * @param  id the id of the {@link DevConsole}
+     * @return    the resolved {@link DevConsole}, or <tt>null</tt> if not found
+     */
+    DevConsole resolveDevConsole(String id);
 
-    @Override
-    public String toString() {
-        return "Step[" + getStepId() + "] started";
-    }
 }
diff --git a/core/camel-api/src/main/java/org/apache/camel/health/HealthCheck.java b/core/camel-api/src/main/java/org/apache/camel/health/HealthCheck.java
index abb8931..59ea3ae 100644
--- a/core/camel-api/src/main/java/org/apache/camel/health/HealthCheck.java
+++ b/core/camel-api/src/main/java/org/apache/camel/health/HealthCheck.java
@@ -98,7 +98,7 @@ public interface HealthCheck extends HasGroup, HasId, Ordered {
      * The implementation is responsible to eventually perform the check according to the limitation of the third party
      * system i.e. it should not be performed too often to avoid rate limiting. The options argument can be used to pass
      * information specific to the check like forcing the check to be performed against the policies. The implementation
-     * is responsible to catch an handle any exception thrown by the underlying technology, including unchecked ones.
+     * is responsible to catch and handle any exception thrown by the underlying technology, including unchecked ones.
      */
     Result call(Map<String, Object> options);
 
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 4d0105d..0ca1b16 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
@@ -80,6 +80,8 @@ import org.apache.camel.TypeConverter;
 import org.apache.camel.VetoCamelContextStartException;
 import org.apache.camel.api.management.JmxSystemPropertyKeys;
 import org.apache.camel.catalog.RuntimeCamelCatalog;
+import org.apache.camel.console.DevConsoleRegistry;
+import org.apache.camel.console.DevConsoleResolver;
 import org.apache.camel.health.HealthCheckRegistry;
 import org.apache.camel.health.HealthCheckResolver;
 import org.apache.camel.spi.AnnotationBasedProcessorFactory;
@@ -262,6 +264,7 @@ public abstract class AbstractCamelContext extends BaseService
     private Boolean disableJMX = Boolean.FALSE;
     private Boolean loadTypeConverters = Boolean.FALSE;
     private Boolean loadHealthChecks = Boolean.FALSE;
+    private Boolean devConsole = Boolean.FALSE;
     private Boolean sourceLocationEnabled = Boolean.FALSE;
     private Boolean typeConverterStatisticsEnabled = Boolean.FALSE;
     private Boolean dumpRoutes = Boolean.FALSE;
@@ -296,6 +299,7 @@ public abstract class AbstractCamelContext extends BaseService
     private volatile UriFactoryResolver uriFactoryResolver;
     private volatile DataFormatResolver dataFormatResolver;
     private volatile HealthCheckResolver healthCheckResolver;
+    private volatile DevConsoleResolver devConsoleResolver;
     private volatile ManagementStrategy managementStrategy;
     private volatile ManagementMBeanAssembler managementMBeanAssembler;
     private volatile RestRegistryFactory restRegistryFactory;
@@ -2698,6 +2702,19 @@ public abstract class AbstractCamelContext extends BaseService
             startupStepRecorder.endStep(step4);
         }
 
+        // setup dev-console registry as its needed this early phase for 3rd party to register custom consoles
+        DevConsoleRegistry dcr = getExtension(DevConsoleRegistry.class);
+        if (dcr == null) {
+            StartupStep step5 = startupStepRecorder.beginStep(CamelContext.class, null, "Setup DevConsoleRegistry");
+            dcr = createDevConsoleRegistry();
+            if (dcr != null) {
+                // install dev-console registry if it was discovered from classpath (camel-console)
+                dcr.setCamelContext(this);
+                setExtension(DevConsoleRegistry.class, dcr);
+            }
+            startupStepRecorder.endStep(step5);
+        }
+
         // Call all registered trackers with this context
         // Note, this may use a partially constructed object
         CamelContextTracker.notifyContextCreated(this);
@@ -2765,6 +2782,15 @@ public abstract class AbstractCamelContext extends BaseService
             }
             startupStepRecorder.endStep(step3);
         }
+        // ensure additional dev consoles is loaded
+        if (devConsole) {
+            StartupStep step4 = startupStepRecorder.beginStep(CamelContext.class, null, "Scan DevConsoles");
+            DevConsoleRegistry dcr = getExtension(DevConsoleRegistry.class);
+            if (dcr != null) {
+                dcr.loadDevConsoles();
+            }
+            startupStepRecorder.endStep(step4);
+        }
 
         // custom properties may use property placeholders so resolve those
         // early on
@@ -4215,6 +4241,15 @@ public abstract class AbstractCamelContext extends BaseService
         this.loadHealthChecks = loadHealthChecks;
     }
 
+    public Boolean isDevConsole() {
+        return devConsole != null && devConsole;
+    }
+
+    @Override
+    public void setDevConsole(Boolean loadDevConsoles) {
+        this.devConsole = loadDevConsoles;
+    }
+
     @Override
     public Boolean isTypeConverterStatisticsEnabled() {
         return typeConverterStatisticsEnabled != null && typeConverterStatisticsEnabled;
@@ -4389,6 +4424,21 @@ public abstract class AbstractCamelContext extends BaseService
         this.healthCheckResolver = doAddService(healthCheckResolver);
     }
 
+    public DevConsoleResolver getDevConsoleResolver() {
+        if (devConsoleResolver == null) {
+            synchronized (lock) {
+                if (devConsoleResolver == null) {
+                    setDevConsoleResolver(createDevConsoleResolver());
+                }
+            }
+        }
+        return devConsoleResolver;
+    }
+
+    public void setDevConsoleResolver(DevConsoleResolver devConsoleResolver) {
+        this.devConsoleResolver = doAddService(devConsoleResolver);
+    }
+
     @Override
     public ShutdownStrategy getShutdownStrategy() {
         if (shutdownStrategy == null) {
@@ -5046,6 +5096,8 @@ public abstract class AbstractCamelContext extends BaseService
 
     protected abstract HealthCheckRegistry createHealthCheckRegistry();
 
+    protected abstract DevConsoleRegistry createDevConsoleRegistry();
+
     protected abstract ReactiveExecutor createReactiveExecutor();
 
     protected abstract StreamCachingStrategy createStreamCachingStrategy();
@@ -5088,6 +5140,8 @@ public abstract class AbstractCamelContext extends BaseService
 
     protected abstract HealthCheckResolver createHealthCheckResolver();
 
+    protected abstract DevConsoleResolver createDevConsoleResolver();
+
     protected abstract MessageHistoryFactory createMessageHistoryFactory();
 
     protected abstract InflightRepository createInflightRepository();
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDevConsoleResolver.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDevConsoleResolver.java
new file mode 100644
index 0000000..16f15bd
--- /dev/null
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDevConsoleResolver.java
@@ -0,0 +1,89 @@
+/*
+ * 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.impl.engine;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.NoFactoryAvailableException;
+import org.apache.camel.console.DevConsole;
+import org.apache.camel.console.DevConsoleResolver;
+import org.apache.camel.spi.FactoryFinder;
+
+/**
+ * Default dev console resolver that looks for dev consoles factories in
+ * <b>META-INF/services/org/apache/camel/dev-console/</b>.
+ */
+public class DefaultDevConsoleResolver implements DevConsoleResolver, CamelContextAware {
+
+    public static final String DEV_CONSOLE_RESOURCE_PATH = "META-INF/services/org/apache/camel/dev-console/";
+
+    protected FactoryFinder devConsoleFactory;
+    private CamelContext camelContext;
+
+    @Override
+    public CamelContext getCamelContext() {
+        return camelContext;
+    }
+
+    @Override
+    public void setCamelContext(CamelContext camelContext) {
+        this.camelContext = camelContext;
+    }
+
+    @Override
+    public DevConsole resolveDevConsole(String id) {
+        // lookup in registry first
+        DevConsole answer = camelContext.getRegistry().lookupByNameAndType(id + "-dev-console", DevConsole.class);
+        if (answer == null) {
+            answer = camelContext.getRegistry().lookupByNameAndType(id, DevConsole.class);
+        }
+        if (answer != null) {
+            return answer;
+        }
+
+        Class<?> type = null;
+        try {
+            type = findDevConsole(id, camelContext);
+        } catch (NoFactoryAvailableException e) {
+            // ignore
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Invalid URI, no DevConsole registered for id: " + id, e);
+        }
+
+        if (type != null) {
+            if (DevConsole.class.isAssignableFrom(type)) {
+                answer = (DevConsole) camelContext.getInjector().newInstance(type, false);
+                CamelContextAware.trySetCamelContext(answer, camelContext);
+            } else {
+                throw new IllegalArgumentException(
+                        "Resolving dev-console: " + id + " detected type conflict: Not a DevConsole implementation. Found: "
+                                                   + type.getName());
+            }
+        }
+
+        return answer;
+    }
+
+    protected Class<?> findDevConsole(String name, CamelContext context) throws Exception {
+        if (devConsoleFactory == null) {
+            devConsoleFactory = context.adapt(ExtendedCamelContext.class).getFactoryFinder(DEV_CONSOLE_RESOURCE_PATH);
+        }
+        return devConsoleFactory.findOptionalClass(name).orElse(null);
+    }
+
+}
diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java
index 8d02c26..cf7e0df 100644
--- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java
+++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java
@@ -26,6 +26,8 @@ import org.apache.camel.Route;
 import org.apache.camel.RouteTemplateContext;
 import org.apache.camel.TypeConverter;
 import org.apache.camel.catalog.RuntimeCamelCatalog;
+import org.apache.camel.console.DevConsoleRegistry;
+import org.apache.camel.console.DevConsoleResolver;
 import org.apache.camel.health.HealthCheckRegistry;
 import org.apache.camel.health.HealthCheckResolver;
 import org.apache.camel.impl.converter.DefaultTypeConverter;
@@ -133,6 +135,17 @@ public class SimpleCamelContext extends AbstractCamelContext {
     }
 
     @Override
+    protected DevConsoleRegistry createDevConsoleRegistry() {
+        Optional<DevConsoleRegistry> result = ResolverHelper.resolveService(
+                getCamelContextReference(),
+                getBootstrapFactoryFinder(),
+                DevConsoleRegistry.FACTORY,
+                DevConsoleRegistry.class);
+
+        return result.orElse(null);
+    }
+
+    @Override
     protected TypeConverter createTypeConverter() {
         return new DefaultTypeConverter(
                 getCamelContextReference(), getPackageScanClassResolver(), getInjector(),
@@ -283,6 +296,11 @@ public class SimpleCamelContext extends AbstractCamelContext {
     }
 
     @Override
+    protected DevConsoleResolver createDevConsoleResolver() {
+        return new DefaultDevConsoleResolver();
+    }
+
+    @Override
     protected MessageHistoryFactory createMessageHistoryFactory() {
         return new DefaultMessageHistoryFactory();
     }
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/CamelContextResumeFailureEvent.java b/core/camel-base/src/main/java/org/apache/camel/impl/event/CamelContextResumeFailureEvent.java
index 5adac86..90e27dc 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/CamelContextResumeFailureEvent.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/event/CamelContextResumeFailureEvent.java
@@ -36,6 +36,6 @@ public class CamelContextResumeFailureEvent extends AbstractContextEvent impleme
 
     @Override
     public String toString() {
-        return "Failed to resume Camel: " + getContext().getName() + " due to " + cause.getMessage();
+        return "Failed to resume CamelContext: " + getContext().getName() + " due to " + cause.getMessage();
     }
 }
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/CamelContextStartupFailureEvent.java b/core/camel-base/src/main/java/org/apache/camel/impl/event/CamelContextStartupFailureEvent.java
index c13ca37..3de5066 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/CamelContextStartupFailureEvent.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/event/CamelContextStartupFailureEvent.java
@@ -37,6 +37,6 @@ public class CamelContextStartupFailureEvent extends AbstractContextEvent
 
     @Override
     public String toString() {
-        return "Failed to start Camel: " + getContext().getName() + " due to " + cause.getMessage();
+        return "CamelContext startup failure: " + getContext().getName() + " due to " + cause.getMessage();
     }
 }
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/CamelContextStopFailureEvent.java b/core/camel-base/src/main/java/org/apache/camel/impl/event/CamelContextStopFailureEvent.java
index 0e1db96..190982c 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/CamelContextStopFailureEvent.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/event/CamelContextStopFailureEvent.java
@@ -36,6 +36,6 @@ public class CamelContextStopFailureEvent extends AbstractContextEvent implement
 
     @Override
     public String toString() {
-        return "Failed to stop Camel: " + getContext().getName() + " due to " + cause.getMessage();
+        return "Failed to stop CamelContext: " + getContext().getName() + " due to " + cause.getMessage();
     }
 }
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteAddedEvent.java b/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteAddedEvent.java
index 8f39424..004cfda 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteAddedEvent.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteAddedEvent.java
@@ -29,6 +29,6 @@ public class RouteAddedEvent extends AbstractRouteEvent implements CamelEvent.Ro
 
     @Override
     public String toString() {
-        return "Added route: " + getRoute().getId();
+        return "Route added: " + getRoute().getId();
     }
 }
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteReloadedEvent.java b/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteReloadedEvent.java
index de2ea8c..eeb5a30 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteReloadedEvent.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteReloadedEvent.java
@@ -44,6 +44,6 @@ public class RouteReloadedEvent extends AbstractRouteEvent implements CamelEvent
 
     @Override
     public String toString() {
-        return "Reloaded route: " + getRoute().getId();
+        return "Route reloaded: " + getRoute().getId();
     }
 }
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteRemovedEvent.java b/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteRemovedEvent.java
index 0cceaac..9f50e16 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteRemovedEvent.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteRemovedEvent.java
@@ -29,6 +29,6 @@ public class RouteRemovedEvent extends AbstractRouteEvent implements CamelEvent.
 
     @Override
     public String toString() {
-        return "Removed route: " + getRoute().getId();
+        return "Route removed: " + getRoute().getId();
     }
 }
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteStartedEvent.java b/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteStartedEvent.java
index 2a2ad1e..6206f44 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteStartedEvent.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteStartedEvent.java
@@ -28,6 +28,6 @@ public class RouteStartedEvent extends AbstractRouteEvent implements CamelEvent.
 
     @Override
     public String toString() {
-        return "Started route: " + getRoute().getId();
+        return "Route started: " + getRoute().getId();
     }
 }
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteStartingEvent.java b/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteStartingEvent.java
index 73c5a8d..19c696c 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteStartingEvent.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteStartingEvent.java
@@ -28,6 +28,6 @@ public class RouteStartingEvent extends AbstractRouteEvent implements CamelEvent
 
     @Override
     public String toString() {
-        return "Starting route: " + getRoute().getId();
+        return "Route starting: " + getRoute().getId();
     }
 }
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteStoppedEvent.java b/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteStoppedEvent.java
index 4dc0008..45e837d 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteStoppedEvent.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteStoppedEvent.java
@@ -28,6 +28,6 @@ public class RouteStoppedEvent extends AbstractRouteEvent implements CamelEvent.
 
     @Override
     public String toString() {
-        return "Stopped route: " + getRoute().getId();
+        return "Route stopped: " + getRoute().getId();
     }
 }
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteStoppingEvent.java b/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteStoppingEvent.java
index 23e626e..24e6771 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteStoppingEvent.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteStoppingEvent.java
@@ -28,6 +28,6 @@ public class RouteStoppingEvent extends AbstractRouteEvent implements CamelEvent
 
     @Override
     public String toString() {
-        return "Stopping route: " + getRoute().getId();
+        return "Route stopping: " + getRoute().getId();
     }
 }
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/ServiceStartupFailureEvent.java b/core/camel-base/src/main/java/org/apache/camel/impl/event/ServiceStartupFailureEvent.java
index 515b878..3095ccf 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/ServiceStartupFailureEvent.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/event/ServiceStartupFailureEvent.java
@@ -51,6 +51,6 @@ public class ServiceStartupFailureEvent extends EventObject implements CamelEven
 
     @Override
     public String toString() {
-        return "Failure to start service: " + service + " due to " + cause.getMessage();
+        return "Service startup failure: " + service + " due to " + cause.getMessage();
     }
 }
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/ServiceStopFailureEvent.java b/core/camel-base/src/main/java/org/apache/camel/impl/event/ServiceStopFailureEvent.java
index f512d26..f904b99 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/ServiceStopFailureEvent.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/event/ServiceStopFailureEvent.java
@@ -51,6 +51,6 @@ public class ServiceStopFailureEvent extends EventObject implements CamelEvent.S
 
     @Override
     public String toString() {
-        return "Failure to stop service: " + service + " due to " + cause.getMessage();
+        return "Service stop failure: " + service + " due to " + cause.getMessage();
     }
 }
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/StepCompletedEvent.java b/core/camel-base/src/main/java/org/apache/camel/impl/event/StepCompletedEvent.java
index d90b195..5020c57 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/StepCompletedEvent.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/event/StepCompletedEvent.java
@@ -30,6 +30,6 @@ public class StepCompletedEvent extends AbstractStepEvent implements CamelEvent.
 
     @Override
     public String toString() {
-        return "Step[" + getStepId() + "] completed";
+        return "Step completed: " + getStepId();
     }
 }
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/StepFailedEvent.java b/core/camel-base/src/main/java/org/apache/camel/impl/event/StepFailedEvent.java
index 89f1df0..17275f1 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/StepFailedEvent.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/event/StepFailedEvent.java
@@ -35,6 +35,6 @@ public class StepFailedEvent extends AbstractStepEvent implements CamelEvent.Ste
 
     @Override
     public String toString() {
-        return "Step[" + getStepId() + "] failed";
+        return "Step failure: " + getStepId() + " due to " + getCause().getMessage();
     }
 }
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/StepStartedEvent.java b/core/camel-base/src/main/java/org/apache/camel/impl/event/StepStartedEvent.java
index 8c3c645..4c5890b 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/StepStartedEvent.java
+++ b/core/camel-base/src/main/java/org/apache/camel/impl/event/StepStartedEvent.java
@@ -30,6 +30,6 @@ public class StepStartedEvent extends AbstractStepEvent implements CamelEvent.St
 
     @Override
     public String toString() {
-        return "Step[" + getStepId() + "] started";
+        return "Step started: " + getStepId();
     }
 }
diff --git a/core/camel-console/pom.xml b/core/camel-console/pom.xml
new file mode 100644
index 0000000..1247615
--- /dev/null
+++ b/core/camel-console/pom.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<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">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>core</artifactId>
+        <version>3.15.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>camel-console</artifactId>
+    <packaging>jar</packaging>
+
+    <name>Camel :: Console</name>
+    <description>Camel Developer Console</description>
+
+    <properties>
+        <firstVersion>3.15.0</firstVersion>
+        <label>core</label>
+        <camel-prepare-component>false</camel-prepare-component>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-core-engine</artifactId>
+        </dependency>
+
+        <!-- testing -->
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-core</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- logging -->
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-slf4j-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+</project>
diff --git a/core/camel-console/src/generated/java/org/apache/camel/impl/console/EventConsoleConfigurer.java b/core/camel-console/src/generated/java/org/apache/camel/impl/console/EventConsoleConfigurer.java
new file mode 100644
index 0000000..4390bad
--- /dev/null
+++ b/core/camel-console/src/generated/java/org/apache/camel/impl/console/EventConsoleConfigurer.java
@@ -0,0 +1,55 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.impl.console;
+
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.ExtendedPropertyConfigurerGetter;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.impl.console.EventConsole;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@SuppressWarnings("unchecked")
+public class EventConsoleConfigurer extends org.apache.camel.support.component.PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+    @Override
+    public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
+        org.apache.camel.impl.console.EventConsole target = (org.apache.camel.impl.console.EventConsole) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "camelcontext":
+        case "CamelContext": target.setCamelContext(property(camelContext, org.apache.camel.CamelContext.class, value)); return true;
+        case "capacity":
+        case "Capacity": target.setCapacity(property(camelContext, int.class, value)); return true;
+        default: return false;
+        }
+    }
+
+    @Override
+    public Class<?> getOptionType(String name, boolean ignoreCase) {
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "camelcontext":
+        case "CamelContext": return org.apache.camel.CamelContext.class;
+        case "capacity":
+        case "Capacity": return int.class;
+        default: return null;
+        }
+    }
+
+    @Override
+    public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+        org.apache.camel.impl.console.EventConsole target = (org.apache.camel.impl.console.EventConsole) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "camelcontext":
+        case "CamelContext": return target.getCamelContext();
+        case "capacity":
+        case "Capacity": return target.getCapacity();
+        default: return null;
+        }
+    }
+}
+
diff --git a/core/camel-console/src/generated/java/org/apache/camel/impl/console/JvmDevConsoleConfigurer.java b/core/camel-console/src/generated/java/org/apache/camel/impl/console/JvmDevConsoleConfigurer.java
new file mode 100644
index 0000000..f93de65
--- /dev/null
+++ b/core/camel-console/src/generated/java/org/apache/camel/impl/console/JvmDevConsoleConfigurer.java
@@ -0,0 +1,55 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.impl.console;
+
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.ExtendedPropertyConfigurerGetter;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.impl.console.JvmDevConsole;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@SuppressWarnings("unchecked")
+public class JvmDevConsoleConfigurer extends org.apache.camel.support.component.PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+    @Override
+    public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
+        org.apache.camel.impl.console.JvmDevConsole target = (org.apache.camel.impl.console.JvmDevConsole) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "camelcontext":
+        case "CamelContext": target.setCamelContext(property(camelContext, org.apache.camel.CamelContext.class, value)); return true;
+        case "showclasspath":
+        case "ShowClasspath": target.setShowClasspath(property(camelContext, boolean.class, value)); return true;
+        default: return false;
+        }
+    }
+
+    @Override
+    public Class<?> getOptionType(String name, boolean ignoreCase) {
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "camelcontext":
+        case "CamelContext": return org.apache.camel.CamelContext.class;
+        case "showclasspath":
+        case "ShowClasspath": return boolean.class;
+        default: return null;
+        }
+    }
+
+    @Override
+    public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+        org.apache.camel.impl.console.JvmDevConsole target = (org.apache.camel.impl.console.JvmDevConsole) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "camelcontext":
+        case "CamelContext": return target.getCamelContext();
+        case "showclasspath":
+        case "ShowClasspath": return target.isShowClasspath();
+        default: return null;
+        }
+    }
+}
+
diff --git a/core/camel-console/src/generated/java/org/apache/camel/impl/console/MemoryDevConsoleConfigurer.java b/core/camel-console/src/generated/java/org/apache/camel/impl/console/MemoryDevConsoleConfigurer.java
new file mode 100644
index 0000000..b5f9088
--- /dev/null
+++ b/core/camel-console/src/generated/java/org/apache/camel/impl/console/MemoryDevConsoleConfigurer.java
@@ -0,0 +1,49 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.impl.console;
+
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.ExtendedPropertyConfigurerGetter;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.impl.console.MemoryDevConsole;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@SuppressWarnings("unchecked")
+public class MemoryDevConsoleConfigurer extends org.apache.camel.support.component.PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+    @Override
+    public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
+        org.apache.camel.impl.console.MemoryDevConsole target = (org.apache.camel.impl.console.MemoryDevConsole) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "camelcontext":
+        case "CamelContext": target.setCamelContext(property(camelContext, org.apache.camel.CamelContext.class, value)); return true;
+        default: return false;
+        }
+    }
+
+    @Override
+    public Class<?> getOptionType(String name, boolean ignoreCase) {
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "camelcontext":
+        case "CamelContext": return org.apache.camel.CamelContext.class;
+        default: return null;
+        }
+    }
+
+    @Override
+    public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+        org.apache.camel.impl.console.MemoryDevConsole target = (org.apache.camel.impl.console.MemoryDevConsole) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "camelcontext":
+        case "CamelContext": return target.getCamelContext();
+        default: return null;
+        }
+    }
+}
+
diff --git a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.EventConsole b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.EventConsole
new file mode 100644
index 0000000..4d40f2f
--- /dev/null
+++ b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.EventConsole
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.console.EventConsoleConfigurer
diff --git a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.JvmDevConsole b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.JvmDevConsole
new file mode 100644
index 0000000..402dacd
--- /dev/null
+++ b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.JvmDevConsole
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.console.JvmDevConsoleConfigurer
diff --git a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.MemoryDevConsole b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.MemoryDevConsole
new file mode 100644
index 0000000..8f1eacc
--- /dev/null
+++ b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.MemoryDevConsole
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.console.MemoryDevConsoleConfigurer
diff --git a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/context b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/context
new file mode 100644
index 0000000..ed3afd7
--- /dev/null
+++ b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/context
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.console.ContextDevConsole
diff --git a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/default-registry b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/default-registry
new file mode 100644
index 0000000..3fac031
--- /dev/null
+++ b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/default-registry
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.console.DefaultDevConsoleRegistry
diff --git a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/event b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/event
new file mode 100644
index 0000000..fbb283c
--- /dev/null
+++ b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/event
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.console.EventConsole
diff --git a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/health b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/health
new file mode 100644
index 0000000..19c9da6
--- /dev/null
+++ b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/health
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.console.HealthDevConsole
diff --git a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/jvm b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/jvm
new file mode 100644
index 0000000..2e662ee
--- /dev/null
+++ b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/jvm
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.console.JvmDevConsole
diff --git a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/memory b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/memory
new file mode 100644
index 0000000..d38602e
--- /dev/null
+++ b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/memory
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.console.MemoryDevConsole
diff --git a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/properties b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/properties
new file mode 100644
index 0000000..9135a88
--- /dev/null
+++ b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/properties
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.console.PropertiesDevConsole
diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/AbstractDevConsole.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/AbstractDevConsole.java
new file mode 100644
index 0000000..5ead0ac
--- /dev/null
+++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/AbstractDevConsole.java
@@ -0,0 +1,114 @@
+/*
+ * 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.impl.console;
+
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.console.DevConsole;
+import org.apache.camel.support.service.ServiceSupport;
+
+/**
+ * Base implementation for {@link DevConsole}.
+ */
+public abstract class AbstractDevConsole extends ServiceSupport implements DevConsole, CamelContextAware {
+
+    private CamelContext camelContext;
+    private final Object lock;
+    private final String group;
+    private final String id;
+    private final String displayName;
+    private final String description;
+
+    public AbstractDevConsole(String group, String id, String displayName, String description) {
+        this.lock = new Object();
+        this.group = group;
+        this.id = id;
+        this.displayName = displayName;
+        this.description = description;
+    }
+
+    @Override
+    public CamelContext getCamelContext() {
+        return camelContext;
+    }
+
+    @Override
+    public void setCamelContext(CamelContext camelContext) {
+        this.camelContext = camelContext;
+    }
+
+    @Override
+    public boolean supportMediaType(MediaType mediaType) {
+        return mediaType == MediaType.TEXT;
+    }
+
+    @Override
+    public String getGroup() {
+        return group;
+    }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    @Override
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    @Override
+    public String getDescription() {
+        return description;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof AbstractDevConsole)) {
+            return false;
+        }
+
+        AbstractDevConsole that = (AbstractDevConsole) o;
+
+        return id.equals(that.id);
+    }
+
+    @Override
+    public int hashCode() {
+        return id != null ? id.hashCode() : 0;
+    }
+
+    @Override
+    public Object call(MediaType mediaType, Map<String, Object> options) {
+        synchronized (lock) {
+            return doCall(mediaType, options);
+        }
+    }
+
+    /**
+     * Invokes and gets the output from this console.
+     *
+     * @see DevConsole#call(MediaType, Map)
+     */
+    protected abstract Object doCall(MediaType mediaType, Map<String, Object> options);
+
+}
diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
new file mode 100644
index 0000000..059d36c
--- /dev/null
+++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
@@ -0,0 +1,53 @@
+/*
+ * 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.impl.console;
+
+import java.util.Map;
+
+import org.apache.camel.api.management.ManagedCamelContext;
+import org.apache.camel.api.management.mbean.ManagedCamelContextMBean;
+import org.apache.camel.spi.annotations.DevConsole;
+
+@DevConsole("context")
+public class ContextDevConsole extends AbstractDevConsole {
+
+    public ContextDevConsole() {
+        super("camel", "context", "CamelContext", "Overall information about the CamelContext");
+    }
+
+    @Override
+    protected Object doCall(MediaType mediaType, Map<String, Object> options) {
+        // only text is supported
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(String.format("Apache Camel %s (%s) uptime %s", getCamelContext().getVersion(), getCamelContext().getName(),
+                getCamelContext().getUptime()));
+        sb.append("\n");
+
+        // TODO: number of messages should maybe be in another console
+        ManagedCamelContext mcc = getCamelContext().getExtension(ManagedCamelContext.class);
+        if (mcc != null) {
+            ManagedCamelContextMBean mb = mcc.getManagedCamelContext();
+            sb.append(String.format("\n    Total: %s", mb.getExchangesTotal()));
+            sb.append(String.format("\n    Failed: %s", mb.getExchangesFailed()));
+            sb.append(String.format("\n    Inflight: %s", mb.getExchangesInflight()));
+            sb.append("\n");
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/DefaultDevConsoleRegistry.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/DefaultDevConsoleRegistry.java
new file mode 100644
index 0000000..7089201
--- /dev/null
+++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/DefaultDevConsoleRegistry.java
@@ -0,0 +1,185 @@
+/*
+ * 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.impl.console;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Stream;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.DeferredContextBinding;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.console.DevConsole;
+import org.apache.camel.console.DevConsoleRegistry;
+import org.apache.camel.console.DevConsoleResolver;
+import org.apache.camel.support.service.ServiceHelper;
+import org.apache.camel.support.service.ServiceSupport;
+import org.apache.camel.util.StopWatch;
+import org.apache.camel.util.TimeUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Default {@link org.apache.camel.console.DevConsoleRegistry}.
+ */
+@org.apache.camel.spi.annotations.DevConsole(DevConsoleRegistry.NAME)
+@DeferredContextBinding
+public class DefaultDevConsoleRegistry extends ServiceSupport implements DevConsoleRegistry {
+
+    private static final Logger LOG = LoggerFactory.getLogger(DefaultDevConsoleRegistry.class);
+
+    private String id = "camel-console";
+    private CamelContext camelContext;
+    private final Set<DevConsole> consoles;
+    private boolean enabled = true;
+    private volatile boolean loadDevConsolesDone;
+
+    public DefaultDevConsoleRegistry() {
+        this(null);
+    }
+
+    public DefaultDevConsoleRegistry(CamelContext camelContext) {
+        // sort by id
+        this.consoles = new TreeSet<>(Comparator.comparing(DevConsole::getId));
+        setCamelContext(camelContext);
+    }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    @Override
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    @Override
+    public CamelContext getCamelContext() {
+        return camelContext;
+    }
+
+    @Override
+    public void setCamelContext(CamelContext camelContext) {
+        this.camelContext = camelContext;
+    }
+
+    @Override
+    protected void doInit() throws Exception {
+        for (DevConsole console : consoles) {
+            CamelContextAware.trySetCamelContext(console, camelContext);
+            ServiceHelper.initService(console);
+        }
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        for (DevConsole console : consoles) {
+            ServiceHelper.startService(console);
+        }
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        for (DevConsole console : consoles) {
+            ServiceHelper.stopService(console);
+        }
+    }
+
+    @Override
+    public DevConsole resolveById(String id) {
+        DevConsole answer = consoles.stream().filter(h -> h.getId().equals(id)).findFirst()
+                .orElse(camelContext.getRegistry().findByTypeWithName(DevConsole.class).get(id));
+        if (answer == null) {
+            DevConsoleResolver resolver = camelContext.adapt(ExtendedCamelContext.class).getDevConsoleResolver();
+            answer = resolver.resolveDevConsole(id);
+            if (answer != null) {
+                register(answer);
+            }
+        }
+
+        return answer;
+    }
+
+    @Override
+    public boolean register(DevConsole console) {
+        boolean result;
+        // do we have this already
+        if (getConsole(console.getId()).isPresent()) {
+            return false;
+        }
+        result = consoles.add(console);
+        if (result) {
+            CamelContextAware.trySetCamelContext(console, camelContext);
+            LOG.debug("DevConsole with id {} successfully registered", console.getId());
+        }
+        return result;
+    }
+
+    @Override
+    public boolean unregister(DevConsole console) {
+        boolean result;
+
+        result = consoles.remove(console);
+        if (result) {
+            LOG.debug("DevConsole with id {} successfully un-registered", console.getId());
+        }
+        return result;
+    }
+
+    @Override
+    public Stream<DevConsole> stream() {
+        if (enabled) {
+            return consoles.stream();
+        }
+        return Stream.empty();
+    }
+
+    @Override
+    public void loadDevConsoles() {
+        StopWatch watch = new StopWatch();
+
+        if (!loadDevConsolesDone) {
+            loadDevConsolesDone = true;
+
+            DefaultDevConsolesLoader loader = new DefaultDevConsolesLoader(camelContext);
+            Collection<DevConsole> col = loader.loadDevConsoles();
+
+            if (col.size() > 0) {
+                // register the loaded consoles
+                for (DevConsole console : col) {
+                    register(console);
+                }
+                String time = TimeUtils.printDuration(watch.taken());
+                LOG.info("Dev consoles (scanned: {}) loaded in {}", col.size(), time);
+            }
+        }
+    }
+}
diff --git a/core/camel-health/src/main/java/org/apache/camel/impl/health/DefaultHealthChecksLoader.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/DefaultDevConsolesLoader.java
similarity index 59%
copy from core/camel-health/src/main/java/org/apache/camel/impl/health/DefaultHealthChecksLoader.java
copy to core/camel-console/src/main/java/org/apache/camel/impl/console/DefaultDevConsolesLoader.java
index c9efaa8..da2c848 100644
--- a/core/camel-health/src/main/java/org/apache/camel/impl/health/DefaultHealthChecksLoader.java
+++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/DefaultDevConsolesLoader.java
@@ -14,15 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.impl.health;
+package org.apache.camel.impl.console;
 
 import java.util.ArrayList;
 import java.util.Collection;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.ExtendedCamelContext;
-import org.apache.camel.health.HealthCheck;
-import org.apache.camel.health.HealthCheckResolver;
+import org.apache.camel.console.DevConsole;
+import org.apache.camel.console.DevConsoleResolver;
 import org.apache.camel.spi.PackageScanResourceResolver;
 import org.apache.camel.spi.Resource;
 import org.apache.camel.util.StringHelper;
@@ -30,47 +30,47 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * To load custom health-checks by classpath scanning.
+ * To load custom {@link org.apache.camel.console.DevConsole} by classpath scanning.
  */
-public class DefaultHealthChecksLoader {
+public class DefaultDevConsolesLoader {
 
-    public static final String META_INF_SERVICES = "META-INF/services/org/apache/camel/health-check";
-    private static final Logger LOG = LoggerFactory.getLogger(DefaultHealthChecksLoader.class);
+    public static final String META_INF_SERVICES = "META-INF/services/org/apache/camel/dev-console";
+    private static final Logger LOG = LoggerFactory.getLogger(DefaultDevConsolesLoader.class);
 
     protected final CamelContext camelContext;
     protected final PackageScanResourceResolver resolver;
-    protected final HealthCheckResolver healthCheckResolver;
+    protected final DevConsoleResolver devConsoleResolver;
 
-    public DefaultHealthChecksLoader(CamelContext camelContext) {
+    public DefaultDevConsolesLoader(CamelContext camelContext) {
         this.camelContext = camelContext;
         this.resolver = camelContext.adapt(ExtendedCamelContext.class).getPackageScanResourceResolver();
-        this.healthCheckResolver = camelContext.adapt(ExtendedCamelContext.class).getHealthCheckResolver();
+        this.devConsoleResolver = camelContext.adapt(ExtendedCamelContext.class).getDevConsoleResolver();
     }
 
-    public Collection<HealthCheck> loadHealthChecks() {
-        Collection<HealthCheck> answer = new ArrayList<>();
+    public Collection<DevConsole> loadDevConsoles() {
+        Collection<DevConsole> answer = new ArrayList<>();
 
-        LOG.trace("Searching for {} health checks", META_INF_SERVICES);
+        LOG.trace("Searching for {} dev consoles", META_INF_SERVICES);
 
         try {
-            Collection<Resource> resources = resolver.findResources(META_INF_SERVICES + "/*-check");
+            Collection<Resource> resources = resolver.findResources(META_INF_SERVICES + "/*");
             if (LOG.isDebugEnabled()) {
-                LOG.debug("Discovered {} health checks from classpath scanning", resources.size());
+                LOG.debug("Discovered {} dev consoles from classpath scanning", resources.size());
             }
             for (Resource resource : resources) {
                 LOG.trace("Resource: {}", resource);
                 if (acceptResource(resource)) {
                     String id = extractId(resource);
-                    LOG.trace("Loading HealthCheck: {}", id);
-                    HealthCheck hc = healthCheckResolver.resolveHealthCheck(id);
-                    if (hc != null) {
-                        LOG.debug("Loaded HealthCheck: {}/{}", hc.getGroup(), hc.getId());
-                        answer.add(hc);
+                    LOG.trace("Loading DevConsole: {}", id);
+                    DevConsole dc = devConsoleResolver.resolveDevConsole(id);
+                    if (dc != null) {
+                        LOG.debug("Loaded DevConsole: {}/{}", dc.getGroup(), dc.getId());
+                        answer.add(dc);
                     }
                 }
             }
         } catch (Exception e) {
-            LOG.warn("Error during scanning for custom health-checks on classpath due to: " + e.getMessage()
+            LOG.warn("Error during scanning for custom dev-consoles on classpath due to: " + e.getMessage()
                      + ". This exception is ignored.");
         }
 
@@ -82,9 +82,8 @@ public class DefaultHealthChecksLoader {
         if (loc == null) {
             return false;
         }
-
-        // this is an out of the box health-check
-        if (loc.endsWith("context-check")) {
+        if (loc.endsWith("default-registry")) {
+            // this is the registry so should be skipped
             return false;
         }
 
@@ -93,12 +92,7 @@ public class DefaultHealthChecksLoader {
 
     protected String extractId(Resource resource) {
         String loc = resource.getLocation();
-        loc = StringHelper.after(loc, META_INF_SERVICES + "/");
-        // remove -check suffix
-        if (loc != null && loc.endsWith("-check")) {
-            loc = loc.substring(0, loc.length() - 6);
-        }
-        return loc;
+        return StringHelper.after(loc, META_INF_SERVICES + "/");
     }
 
 }
diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/EventConsole.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/EventConsole.java
new file mode 100644
index 0000000..ed09bcc
--- /dev/null
+++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/EventConsole.java
@@ -0,0 +1,111 @@
+/*
+ * 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.impl.console;
+
+import java.util.ArrayDeque;
+import java.util.Map;
+import java.util.Queue;
+
+import org.apache.camel.spi.CamelEvent;
+import org.apache.camel.spi.Configurer;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.annotations.DevConsole;
+import org.apache.camel.support.EventNotifierSupport;
+
+@DevConsole("event")
+@Configurer(bootstrap = true)
+public class EventConsole extends AbstractDevConsole {
+
+    @Metadata(defaultValue = "25", description = "Maximum capacity of last number of events to capture")
+    private int capacity = 25;
+
+    private Queue<CamelEvent> events;
+    private Queue<CamelEvent.ExchangeEvent> exchangeEvents;
+    private final ConsoleEventNotifier listener = new ConsoleEventNotifier();
+
+    public EventConsole() {
+        super("camel", "event", "Camel Events", "The most recent Camel events");
+    }
+
+    public int getCapacity() {
+        return capacity;
+    }
+
+    public void setCapacity(int capacity) {
+        this.capacity = capacity;
+    }
+
+    @Override
+    protected void doInit() throws Exception {
+        this.events = new ArrayDeque<>(capacity);
+        this.exchangeEvents = new ArrayDeque<>(capacity);
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        getCamelContext().getManagementStrategy().addEventNotifier(listener);
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        getCamelContext().getManagementStrategy().removeEventNotifier(listener);
+        events.clear();
+    }
+
+    @Override
+    protected Object doCall(MediaType mediaType, Map<String, Object> options) {
+        // only text is supported
+        StringBuilder sb = new StringBuilder();
+
+        if (!events.isEmpty()) {
+            sb.append(String.format("Last %s Camel Events:", events.size()));
+            for (CamelEvent event : events) {
+                sb.append(String.format("\n    %s", event));
+            }
+            sb.append("\n");
+        }
+        if (!exchangeEvents.isEmpty()) {
+            sb.append("\n");
+            sb.append(String.format("Last %s Exchange Events:", exchangeEvents.size()));
+            for (CamelEvent.ExchangeEvent event : exchangeEvents) {
+                sb.append(String.format("\n    %s", event));
+            }
+            sb.append("\n");
+        }
+
+        return sb.toString();
+    }
+
+    private class ConsoleEventNotifier extends EventNotifierSupport {
+
+        @Override
+        public void notify(CamelEvent event) throws Exception {
+            if (event instanceof CamelEvent.ExchangeEvent) {
+                if (exchangeEvents.size() >= capacity) {
+                    exchangeEvents.poll();
+                }
+                exchangeEvents.add((CamelEvent.ExchangeEvent) event);
+            } else {
+                if (events.size() >= capacity) {
+                    events.poll();
+                }
+                events.offer(event);
+            }
+        }
+
+    }
+}
diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/HealthDevConsole.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/HealthDevConsole.java
new file mode 100644
index 0000000..914e6d8
--- /dev/null
+++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/HealthDevConsole.java
@@ -0,0 +1,65 @@
+/*
+ * 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.impl.console;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.camel.health.HealthCheck;
+import org.apache.camel.health.HealthCheckHelper;
+import org.apache.camel.spi.annotations.DevConsole;
+
+@DevConsole("health")
+public class HealthDevConsole extends AbstractDevConsole {
+
+    public HealthDevConsole() {
+        super("camel", "health", "Health Check", "Health Check Status");
+    }
+
+    @Override
+    protected Object doCall(MediaType mediaType, Map<String, Object> options) {
+        // only text is supported
+        StringBuilder sb = new StringBuilder();
+
+        Collection<HealthCheck.Result> results = HealthCheckHelper.invoke(getCamelContext());
+        boolean up = results.stream().allMatch(h -> HealthCheck.State.UP.equals(h.getState()));
+        sb.append(String.format("Health Check Status: %s", up ? "UP" : "DOWN"));
+        sb.append("\n");
+
+        results.forEach(res -> {
+            boolean ok = res.getState().equals(HealthCheck.State.UP);
+            if (ok) {
+                sb.append(String.format("\n    %s: %s", res.getCheck().getId(), res.getState()));
+            } else {
+                String msg = res.getMessage().orElse("");
+                sb.append(String.format("\n    %s: %s (%s)", res.getCheck().getId(), res.getState(), msg));
+                Throwable cause = res.getError().orElse(null);
+                if (cause != null) {
+                    StringWriter sw = new StringWriter();
+                    PrintWriter pw = new PrintWriter(sw);
+                    cause.printStackTrace(pw);
+                    sb.append(pw);
+                    sb.append("\n\n");
+                }
+            }
+        });
+
+        return sb.toString();
+    }
+}
diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/JvmDevConsole.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/JvmDevConsole.java
new file mode 100644
index 0000000..5534fcd
--- /dev/null
+++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/JvmDevConsole.java
@@ -0,0 +1,76 @@
+/*
+ * 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.impl.console;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
+import java.util.Map;
+
+import org.apache.camel.spi.Configurer;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.annotations.DevConsole;
+import org.apache.camel.util.TimeUtils;
+
+@DevConsole("jvm")
+@Configurer(bootstrap = true)
+public class JvmDevConsole extends AbstractDevConsole {
+
+    @Metadata(defaultValue = "true", description = "Show classpath information")
+    private boolean showClasspath = true;
+
+    public JvmDevConsole() {
+        super("jvm", "jvm", "JVM", "Displays JVM information");
+    }
+
+    public boolean isShowClasspath() {
+        return showClasspath;
+    }
+
+    public void setShowClasspath(boolean showClasspath) {
+        this.showClasspath = showClasspath;
+    }
+
+    @Override
+    protected Object doCall(MediaType mediaType, Map<String, Object> options) {
+        // only text is supported
+        StringBuilder sb = new StringBuilder();
+
+        RuntimeMXBean mb = ManagementFactory.getRuntimeMXBean();
+        if (mb != null) {
+            sb.append(String.format("Java Name: %s\n", mb.getVmName()));
+            sb.append(String.format("Java Version: %s\n", mb.getVmVersion()));
+            sb.append(String.format("Java Vendor: %s\n", mb.getVmVendor()));
+            sb.append(String.format("Uptime: %s\n", TimeUtils.printDuration(mb.getUptime())));
+            sb.append(String.format("PID: %s\n", mb.getPid()));
+            if (!mb.getInputArguments().isEmpty()) {
+                sb.append("Input Arguments:");
+                String cp = String.join("\n    ", mb.getInputArguments());
+                sb.append("\n    ").append(cp).append("\n");
+            }
+            if (mb.isBootClassPathSupported()) {
+                sb.append("Boot Classpath:");
+                String cp = String.join("\n    ", mb.getBootClassPath().split("[:|;]"));
+                sb.append("\n    ").append(cp).append("\n");
+            }
+            sb.append("Classpath:");
+            String cp = String.join("\n    ", mb.getClassPath().split("[:|;]"));
+            sb.append("\n    ").append(cp).append("\n");
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/MemoryDevConsole.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/MemoryDevConsole.java
new file mode 100644
index 0000000..1fa9c0c
--- /dev/null
+++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/MemoryDevConsole.java
@@ -0,0 +1,58 @@
+/*
+ * 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.impl.console;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryMXBean;
+import java.util.Map;
+
+import org.apache.camel.spi.Configurer;
+import org.apache.camel.spi.annotations.DevConsole;
+
+import static org.apache.camel.util.UnitUtils.printUnitFromBytesDot;
+
+@DevConsole("memory")
+@Configurer(bootstrap = true)
+public class MemoryDevConsole extends AbstractDevConsole {
+
+    public MemoryDevConsole() {
+        super("jvm", "memory", "JVM Memory", "Displays JVM memory information");
+    }
+
+    @Override
+    protected Object doCall(MediaType mediaType, Map<String, Object> options) {
+        // only text is supported
+        StringBuilder sb = new StringBuilder();
+
+        MemoryMXBean mb = ManagementFactory.getMemoryMXBean();
+        if (mb != null) {
+            sb.append(String.format("Heap Init: %s\n", printUnitFromBytesDot(mb.getHeapMemoryUsage().getInit())));
+            sb.append(String.format("Heap Max: %s\n", printUnitFromBytesDot(mb.getHeapMemoryUsage().getMax())));
+            sb.append(String.format("Heap Used: %s\n", printUnitFromBytesDot(mb.getHeapMemoryUsage().getUsed())));
+            sb.append(String.format("Heap Committed: %s\n", printUnitFromBytesDot(mb.getHeapMemoryUsage().getCommitted())));
+            sb.append("\n");
+            sb.append(String.format("Non-Heap Init: %s\n", printUnitFromBytesDot(mb.getNonHeapMemoryUsage().getInit())));
+            sb.append(String.format("Non-Heap Max: %s\n", printUnitFromBytesDot(mb.getNonHeapMemoryUsage().getMax())));
+            sb.append(String.format("Non-Heap Used: %s\n", printUnitFromBytesDot(mb.getNonHeapMemoryUsage().getUsed())));
+            sb.append(String.format("Non-Heap Committed: %s\n",
+                    printUnitFromBytesDot(mb.getNonHeapMemoryUsage().getCommitted())));
+        }
+
+        return sb.toString();
+    }
+
+}
diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/PropertiesDevConsole.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/PropertiesDevConsole.java
new file mode 100644
index 0000000..7a4bde0
--- /dev/null
+++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/PropertiesDevConsole.java
@@ -0,0 +1,49 @@
+/*
+ * 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.impl.console;
+
+import java.util.Map;
+
+import org.apache.camel.spi.PropertiesComponent;
+import org.apache.camel.spi.annotations.DevConsole;
+
+@DevConsole("properties")
+public class PropertiesDevConsole extends AbstractDevConsole {
+
+    public PropertiesDevConsole() {
+        super("camel", "properties", "Properties", "Displays the properties loaded by Camel");
+    }
+
+    @Override
+    protected Object doCall(MediaType mediaType, Map<String, Object> options) {
+        // only text is supported
+        StringBuilder sb = new StringBuilder();
+
+        PropertiesComponent pc = getCamelContext().getPropertiesComponent();
+        String loc = String.join(", ", pc.getLocations());
+        sb.append(String.format("Properties loaded from locations: %s", loc));
+        sb.append("\n");
+        for (Map.Entry<Object, Object> entry : pc.loadProperties().entrySet()) {
+            Object k = entry.getKey();
+            Object v = entry.getValue();
+            sb.append(String.format("\n    %s = %s", k, v));
+        }
+        sb.append("\n");
+
+        return sb.toString();
+    }
+}
diff --git a/core/camel-console/src/test/java/org/apache/camel/impl/console/ContextDevConsoleTest.java b/core/camel-console/src/test/java/org/apache/camel/impl/console/ContextDevConsoleTest.java
new file mode 100644
index 0000000..ff0d9e2
--- /dev/null
+++ b/core/camel-console/src/test/java/org/apache/camel/impl/console/ContextDevConsoleTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.impl.console;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.console.DevConsole;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class ContextDevConsoleTest extends ContextTestSupport {
+
+    @Test
+    public void testContext() throws Exception {
+        DevConsole con = context.adapt(ExtendedCamelContext.class).getDevConsoleResolver().resolveDevConsole("context");
+        Assertions.assertNotNull(con);
+        Assertions.assertEquals("camel", con.getGroup());
+        Assertions.assertEquals("context", con.getId());
+
+        String out = (String) con.call(DevConsole.MediaType.TEXT);
+        Assertions.assertNotNull(out);
+        log.info(out);
+        Assertions.assertTrue(out.contains(context.getName()));
+    }
+}
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteStartedEvent.java b/core/camel-console/src/test/java/org/apache/camel/impl/console/DefaultDevConsolesLoaderTest.java
similarity index 59%
copy from core/camel-base/src/main/java/org/apache/camel/impl/event/RouteStartedEvent.java
copy to core/camel-console/src/test/java/org/apache/camel/impl/console/DefaultDevConsolesLoaderTest.java
index 2a2ad1e..3f768ed 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/RouteStartedEvent.java
+++ b/core/camel-console/src/test/java/org/apache/camel/impl/console/DefaultDevConsolesLoaderTest.java
@@ -14,20 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.impl.event;
+package org.apache.camel.impl.console;
 
-import org.apache.camel.Route;
-import org.apache.camel.spi.CamelEvent;
+import java.util.Collection;
 
-public class RouteStartedEvent extends AbstractRouteEvent implements CamelEvent.RouteStartedEvent {
-    private static final long serialVersionUID = 1330257282431407329L;
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.console.DevConsole;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
 
-    public RouteStartedEvent(Route source) {
-        super(source);
-    }
+public class DefaultDevConsolesLoaderTest extends ContextTestSupport {
 
-    @Override
-    public String toString() {
-        return "Started route: " + getRoute().getId();
+    @Test
+    public void testLoader() throws Exception {
+        DefaultDevConsolesLoader loader = new DefaultDevConsolesLoader(context);
+        Collection<DevConsole> col = loader.loadDevConsoles();
+        Assertions.assertTrue(col.size() > 3);
     }
 }
diff --git a/core/camel-console/src/test/resources/log4j2.properties b/core/camel-console/src/test/resources/log4j2.properties
new file mode 100644
index 0000000..5642469
--- /dev/null
+++ b/core/camel-console/src/test/resources/log4j2.properties
@@ -0,0 +1,33 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+appender.console.type = Console
+appender.console.name = console
+appender.console.layout.type = PatternLayout
+appender.console.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
+
+appender.file.type = File
+appender.file.name = file
+appender.file.fileName = target/camel-console-test.log
+appender.file.append = true
+appender.file.layout.type = PatternLayout
+appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
+
+rootLogger.level = INFO
+
+rootLogger.appenderRef.file.ref = file
+#rootLogger.appenderRef.console.ref = console
+
diff --git a/core/camel-core-engine/src/generated/java/org/apache/camel/impl/ExtendedCamelContextConfigurer.java b/core/camel-core-engine/src/generated/java/org/apache/camel/impl/ExtendedCamelContextConfigurer.java
index 6a416f31..af12c3d 100644
--- a/core/camel-core-engine/src/generated/java/org/apache/camel/impl/ExtendedCamelContextConfigurer.java
+++ b/core/camel-core-engine/src/generated/java/org/apache/camel/impl/ExtendedCamelContextConfigurer.java
@@ -63,6 +63,10 @@ public class ExtendedCamelContextConfigurer extends org.apache.camel.support.com
         case "DeferServiceFactory": target.setDeferServiceFactory(property(camelContext, org.apache.camel.spi.DeferServiceFactory.class, value)); return true;
         case "delayer":
         case "Delayer": target.setDelayer(property(camelContext, java.lang.Long.class, value)); return true;
+        case "devconsole":
+        case "DevConsole": target.setDevConsole(property(camelContext, java.lang.Boolean.class, value)); return true;
+        case "devconsoleresolver":
+        case "DevConsoleResolver": target.setDevConsoleResolver(property(camelContext, org.apache.camel.console.DevConsoleResolver.class, value)); return true;
         case "dumproutes":
         case "DumpRoutes": target.setDumpRoutes(property(camelContext, java.lang.Boolean.class, value)); return true;
         case "errorhandlerfactory":
@@ -252,6 +256,10 @@ public class ExtendedCamelContextConfigurer extends org.apache.camel.support.com
         case "DeferServiceFactory": return org.apache.camel.spi.DeferServiceFactory.class;
         case "delayer":
         case "Delayer": return java.lang.Long.class;
+        case "devconsole":
+        case "DevConsole": return java.lang.Boolean.class;
+        case "devconsoleresolver":
+        case "DevConsoleResolver": return org.apache.camel.console.DevConsoleResolver.class;
         case "dumproutes":
         case "DumpRoutes": return java.lang.Boolean.class;
         case "errorhandlerfactory":
@@ -442,6 +450,10 @@ public class ExtendedCamelContextConfigurer extends org.apache.camel.support.com
         case "DeferServiceFactory": return target.getDeferServiceFactory();
         case "delayer":
         case "Delayer": return target.getDelayer();
+        case "devconsole":
+        case "DevConsole": return target.isDevConsole();
+        case "devconsoleresolver":
+        case "DevConsoleResolver": return target.getDevConsoleResolver();
         case "dumproutes":
         case "DumpRoutes": return target.isDumpRoutes();
         case "errorhandlerfactory":
diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java
index 537134e..947801b 100644
--- a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java
+++ b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java
@@ -54,6 +54,7 @@ import org.apache.camel.TypeConverter;
 import org.apache.camel.ValueHolder;
 import org.apache.camel.builder.AdviceWithRouteBuilder;
 import org.apache.camel.catalog.RuntimeCamelCatalog;
+import org.apache.camel.console.DevConsoleResolver;
 import org.apache.camel.health.HealthCheckResolver;
 import org.apache.camel.impl.DefaultCamelContext;
 import org.apache.camel.model.DataFormatDefinition;
@@ -910,6 +911,16 @@ public class LightweightCamelContext implements ExtendedCamelContext, CatalogCam
     }
 
     @Override
+    public Boolean isDevConsole() {
+        return delegate.isDevConsole();
+    }
+
+    @Override
+    public void setDevConsole(Boolean loadDevConsoles) {
+        delegate.setDevConsole(loadDevConsoles);
+    }
+
+    @Override
     public Boolean isDumpRoutes() {
         return delegate.isDumpRoutes();
     }
@@ -1318,6 +1329,16 @@ public class LightweightCamelContext implements ExtendedCamelContext, CatalogCam
     }
 
     @Override
+    public DevConsoleResolver getDevConsoleResolver() {
+        return getExtendedCamelContext().getDevConsoleResolver();
+    }
+
+    @Override
+    public void setDevConsoleResolver(DevConsoleResolver devConsoleResolver) {
+        getExtendedCamelContext().setDevConsoleResolver(devConsoleResolver);
+    }
+
+    @Override
     public PackageScanClassResolver getPackageScanClassResolver() {
         return getExtendedCamelContext().getPackageScanClassResolver();
     }
diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightRuntimeCamelContext.java b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightRuntimeCamelContext.java
index fc3ac5e..398d92c 100644
--- a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightRuntimeCamelContext.java
+++ b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightRuntimeCamelContext.java
@@ -62,6 +62,7 @@ import org.apache.camel.StartupSummaryLevel;
 import org.apache.camel.TypeConverter;
 import org.apache.camel.ValueHolder;
 import org.apache.camel.catalog.RuntimeCamelCatalog;
+import org.apache.camel.console.DevConsoleResolver;
 import org.apache.camel.health.HealthCheckResolver;
 import org.apache.camel.impl.converter.CoreTypeConverterRegistry;
 import org.apache.camel.impl.engine.DefaultComponentResolver;
@@ -167,6 +168,7 @@ public class LightweightRuntimeCamelContext implements ExtendedCamelContext, Cat
     private final LanguageResolver languageResolver;
     private final DataFormatResolver dataFormatResolver;
     private final HealthCheckResolver healthCheckResolver;
+    private final DevConsoleResolver devConsoleResolver;
     private final UuidGenerator uuidGenerator;
     private final EndpointRegistry<? extends ValueHolder<String>> endpoints;
     private final Map<String, Component> components;
@@ -218,6 +220,7 @@ public class LightweightRuntimeCamelContext implements ExtendedCamelContext, Cat
         languageResolver = context.adapt(ExtendedCamelContext.class).getLanguageResolver();
         dataFormatResolver = context.adapt(ExtendedCamelContext.class).getDataFormatResolver();
         healthCheckResolver = context.adapt(ExtendedCamelContext.class).getHealthCheckResolver();
+        devConsoleResolver = context.adapt(ExtendedCamelContext.class).getDevConsoleResolver();
         endpoints = context.getEndpointRegistry();
         components = context.getComponentNames().stream().collect(Collectors.toMap(s -> s, context::hasComponent));
         languages = context.getLanguageNames().stream().collect(Collectors.toMap(s -> s, context::resolveLanguage));
@@ -583,6 +586,16 @@ public class LightweightRuntimeCamelContext implements ExtendedCamelContext, Cat
     }
 
     @Override
+    public DevConsoleResolver getDevConsoleResolver() {
+        return devConsoleResolver;
+    }
+
+    @Override
+    public void setDevConsoleResolver(DevConsoleResolver devConsoleResolver) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public UuidGenerator getUuidGenerator() {
         return uuidGenerator;
     }
@@ -1200,6 +1213,16 @@ public class LightweightRuntimeCamelContext implements ExtendedCamelContext, Cat
     }
 
     @Override
+    public Boolean isDevConsole() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setDevConsole(Boolean loadDevConsoles) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public void setLoadHealthChecks(Boolean loadHealthChecks) {
         throw new UnsupportedOperationException();
     }
diff --git a/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java
index 998ad6b..3760a22 100644
--- a/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java
+++ b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java
@@ -49,6 +49,8 @@ import org.apache.camel.cluster.CamelClusterService;
 import org.apache.camel.component.properties.PropertiesComponent;
 import org.apache.camel.component.properties.PropertiesLocation;
 import org.apache.camel.component.properties.PropertiesParser;
+import org.apache.camel.console.DevConsole;
+import org.apache.camel.console.DevConsoleRegistry;
 import org.apache.camel.health.HealthCheckRegistry;
 import org.apache.camel.health.HealthCheckRepository;
 import org.apache.camel.impl.debugger.BacklogTracer;
@@ -409,6 +411,25 @@ public abstract class AbstractCamelContextFactoryBean<T extends ModelCamelContex
                 }
             }
         }
+        // Dev console registry
+        DevConsoleRegistry devConsoleRegistry = getBeanForType(DevConsoleRegistry.class);
+        if (devConsoleRegistry != null) {
+            devConsoleRegistry.setCamelContext(getContext());
+            LOG.debug("Using DevConsoleRegistry: {}", devConsoleRegistry);
+            getContext().setExtension(DevConsoleRegistry.class, devConsoleRegistry);
+        } else {
+            // okay attempt to inject this camel context into existing dev console (if any)
+            devConsoleRegistry = DevConsoleRegistry.get(getContext());
+            if (devConsoleRegistry != null) {
+                devConsoleRegistry.setCamelContext(getContext());
+            }
+        }
+        if (devConsoleRegistry != null) {
+            Set<DevConsole> consoles = getContext().getRegistry().findByType(DevConsole.class);
+            for (DevConsole console : consoles) {
+                devConsoleRegistry.register(console);
+            }
+        }
         // UuidGenerator
         UuidGenerator uuidGenerator = getBeanForType(UuidGenerator.class);
         if (uuidGenerator != null) {
diff --git a/core/camel-core/src/test/java/org/apache/camel/impl/event/EventNotifierServiceStoppingFailedEventTest.java b/core/camel-core/src/test/java/org/apache/camel/impl/event/EventNotifierServiceStoppingFailedEventTest.java
index b8db247..14948b5 100644
--- a/core/camel-core/src/test/java/org/apache/camel/impl/event/EventNotifierServiceStoppingFailedEventTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/impl/event/EventNotifierServiceStoppingFailedEventTest.java
@@ -78,7 +78,7 @@ public class EventNotifierServiceStoppingFailedEventTest extends ContextTestSupp
         assertIsInstanceOf(CamelContextStoppedEvent.class, events.get(8));
 
         assertEquals("Fail B", event.getCause().getMessage());
-        assertEquals("Failure to stop service: B due to Fail B", event.toString());
+        assertEquals("Service stop failure: B due to Fail B", event.toString());
     }
 
     private static final class MyService implements Service {
diff --git a/core/camel-health/src/main/java/org/apache/camel/impl/health/ContextHealthCheck.java b/core/camel-health/src/main/java/org/apache/camel/impl/health/ContextHealthCheck.java
index 622451b..11e3729 100644
--- a/core/camel-health/src/main/java/org/apache/camel/impl/health/ContextHealthCheck.java
+++ b/core/camel-health/src/main/java/org/apache/camel/impl/health/ContextHealthCheck.java
@@ -27,23 +27,12 @@ import org.apache.camel.health.HealthCheckResultBuilder;
  */
 @org.apache.camel.spi.annotations.HealthCheck("context-check")
 public final class ContextHealthCheck extends AbstractHealthCheck {
-    private CamelContext camelContext;
 
     public ContextHealthCheck() {
         super("camel", "context");
     }
 
     @Override
-    public CamelContext getCamelContext() {
-        return camelContext;
-    }
-
-    @Override
-    public void setCamelContext(CamelContext camelContext) {
-        this.camelContext = camelContext;
-    }
-
-    @Override
     public boolean isLiveness() {
         // this check is only for readiness
         return false;
@@ -53,14 +42,14 @@ public final class ContextHealthCheck extends AbstractHealthCheck {
     protected void doCall(HealthCheckResultBuilder builder, Map<String, Object> options) {
         builder.unknown();
 
-        if (camelContext != null) {
-            builder.detail("context.name", camelContext.getName());
-            builder.detail("context.version", camelContext.getVersion());
-            builder.detail("context.status", camelContext.getStatus().name());
+        if (getCamelContext() != null) {
+            builder.detail("context.name", getCamelContext().getName());
+            builder.detail("context.version", getCamelContext().getVersion());
+            builder.detail("context.status", getCamelContext().getStatus().name());
 
-            if (camelContext.getStatus().isStarted()) {
+            if (getCamelContext().getStatus().isStarted()) {
                 builder.up();
-            } else if (camelContext.getStatus().isStopped()) {
+            } else if (getCamelContext().getStatus().isStopped()) {
                 builder.down();
             }
         }
diff --git a/core/camel-health/src/main/java/org/apache/camel/impl/health/DefaultHealthChecksLoader.java b/core/camel-health/src/main/java/org/apache/camel/impl/health/DefaultHealthChecksLoader.java
index c9efaa8..069f838 100644
--- a/core/camel-health/src/main/java/org/apache/camel/impl/health/DefaultHealthChecksLoader.java
+++ b/core/camel-health/src/main/java/org/apache/camel/impl/health/DefaultHealthChecksLoader.java
@@ -30,7 +30,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * To load custom health-checks by classpath scanning.
+ * To load custom {@link HealthCheck} by classpath scanning.
  */
 public class DefaultHealthChecksLoader {
 
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 dc4b742..c508310 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
@@ -55,6 +55,8 @@ public class MainConfigurationPropertiesConfigurer extends org.apache.camel.supp
         case "ConsumerTemplateCacheSize": target.setConsumerTemplateCacheSize(property(camelContext, int.class, value)); return true;
         case "debugging":
         case "Debugging": target.setDebugging(property(camelContext, boolean.class, value)); return true;
+        case "devconsoleenabled":
+        case "DevConsoleEnabled": target.setDevConsoleEnabled(property(camelContext, boolean.class, value)); return true;
         case "dumproutes":
         case "DumpRoutes": target.setDumpRoutes(property(camelContext, boolean.class, value)); return true;
         case "durationhitexitcode":
@@ -274,6 +276,8 @@ public class MainConfigurationPropertiesConfigurer extends org.apache.camel.supp
         case "ConsumerTemplateCacheSize": return int.class;
         case "debugging":
         case "Debugging": return boolean.class;
+        case "devconsoleenabled":
+        case "DevConsoleEnabled": return boolean.class;
         case "dumproutes":
         case "DumpRoutes": return boolean.class;
         case "durationhitexitcode":
@@ -494,6 +498,8 @@ public class MainConfigurationPropertiesConfigurer extends org.apache.camel.supp
         case "ConsumerTemplateCacheSize": return target.getConsumerTemplateCacheSize();
         case "debugging":
         case "Debugging": return target.isDebugging();
+        case "devconsoleenabled":
+        case "DevConsoleEnabled": return target.isDevConsoleEnabled();
         case "dumproutes":
         case "DumpRoutes": return target.isDumpRoutes();
         case "durationhitexitcode":
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 ea0b01f..f68a484 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
@@ -27,6 +27,7 @@
     { "name": "camel.main.configurations", "description": "Sets the configuration objects used to configure the camel context.", "sourceType": "org.apache.camel.main.MainConfigurationProperties", "type": "object", "javaType": "java.util.List" },
     { "name": "camel.main.consumerTemplateCacheSize", "description": "Consumer template endpoints cache size.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "integer", "javaType": "int", "defaultValue": 1000 },
     { "name": "camel.main.debugging", "description": "Sets whether debugging is enabled or not. Default is false.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" },
+    { "name": "camel.main.devConsoleEnabled", "description": "Whether to enable developer console (requires camel-console on classpath). The developer console is only for assisting during development. This is NOT for production usage.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" },
     { "name": "camel.main.dumpRoutes", "description": "If dumping is enabled then Camel will during startup dump all loaded routes (incl rests and route templates) represented as XML DSL into the log. This is intended for trouble shooting or to assist during development. Sensitive information that may be configured in the route endpoints could potentially be included in the dump output and is therefore not recommended to be used for production usage. This requires to have camel-xml-jaxb  [...]
     { "name": "camel.main.durationHitExitCode", "description": "Sets the exit code for the application if duration was hit", "sourceType": "org.apache.camel.main.MainConfigurationProperties", "type": "integer", "javaType": "int" },
     { "name": "camel.main.durationMaxAction", "description": "Controls whether the Camel application should shutdown the JVM, or stop all routes, when duration max is triggered.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "string", "javaType": "java.lang.String", "defaultValue": "shutdown", "enum": [ "shutdown", "stop" ] },
diff --git a/core/camel-main/src/main/docs/main.adoc b/core/camel-main/src/main/docs/main.adoc
index f17bbbc..28c35f3 100644
--- a/core/camel-main/src/main/docs/main.adoc
+++ b/core/camel-main/src/main/docs/main.adoc
@@ -21,7 +21,7 @@ The following tables lists all the options:
 
 // main options: START
 === Camel Main configurations
-The camel.main supports 106 options, which are listed below.
+The camel.main supports 107 options, which are listed below.
 
 [width="100%",cols="2,5,^1,2",options="header"]
 |===
@@ -43,6 +43,7 @@ The camel.main supports 106 options, which are listed below.
 | *camel.main.configurations* | Sets the configuration objects used to configure the camel context. |  | List
 | *camel.main.consumerTemplate{zwsp}CacheSize* | Consumer template endpoints cache size. | 1000 | int
 | *camel.main.debugging* | Sets whether debugging is enabled or not. Default is false. | false | boolean
+| *camel.main.devConsoleEnabled* | Whether to enable developer console (requires camel-console on classpath). The developer console is only for assisting during development. This is NOT for production usage. | false | boolean
 | *camel.main.dumpRoutes* | If dumping is enabled then Camel will during startup dump all loaded routes (incl rests and route templates) represented as XML DSL into the log. This is intended for trouble shooting or to assist during development. Sensitive information that may be configured in the route endpoints could potentially be included in the dump output and is therefore not recommended to be used for production usage. This requires to have camel-xml-jaxb on the classpath to be able [...]
 | *camel.main.durationHitExitCode* | Sets the exit code for the application if duration was hit |  | int
 | *camel.main.durationMaxAction* | Controls whether the Camel application should shutdown the JVM, or stop all routes, when duration max is triggered. | shutdown | String
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 088d4a4..1d68bc6 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
@@ -41,6 +41,8 @@ import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.NoSuchLanguageException;
 import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.StartupStep;
+import org.apache.camel.console.DevConsole;
+import org.apache.camel.console.DevConsoleRegistry;
 import org.apache.camel.health.HealthCheck;
 import org.apache.camel.health.HealthCheckConfiguration;
 import org.apache.camel.health.HealthCheckRegistry;
@@ -721,6 +723,7 @@ public abstract class BaseMainSupport extends BaseService {
         Map<String, Object> lraProperties = new LinkedHashMap<>();
         Map<String, Object> routeTemplateProperties = new LinkedHashMap<>();
         Map<String, Object> beansProperties = new LinkedHashMap<>();
+        Map<String, Object> devConsoleProperties = new LinkedHashMap<>();
         Map<String, String> globalOptions = new LinkedHashMap<>();
         for (String key : prop.stringPropertyNames()) {
             if (key.startsWith("camel.context.")) {
@@ -777,6 +780,12 @@ public abstract class BaseMainSupport extends BaseService {
                 String option = key.substring(20);
                 validateOptionAndValue(key, option, value);
                 routeTemplateProperties.put(optionKey(option), value);
+            } else if (key.startsWith("camel.dev-console.")) {
+                // grab the value
+                String value = prop.getProperty(key);
+                String option = key.substring(18);
+                validateOptionAndValue(key, option, value);
+                devConsoleProperties.put(optionKey(option), value);
             } else if (key.startsWith("camel.beans.")) {
                 // grab the value
                 String value = prop.getProperty(key);
@@ -786,7 +795,7 @@ public abstract class BaseMainSupport extends BaseService {
             } else if (key.startsWith("camel.global-options.")) {
                 // grab the value
                 String value = prop.getProperty(key);
-                String option = key.substring(12);
+                String option = key.substring(21);
                 validateOptionAndValue(key, option, value);
                 globalOptions.put(optionKey(option), value);
             }
@@ -822,7 +831,9 @@ public abstract class BaseMainSupport extends BaseService {
             MainSupportModelConfigurer.setThreadPoolProperties(camelContext, mainConfigurationProperties, threadPoolProperties,
                     mainConfigurationProperties.isAutoConfigurationFailFast(), autoConfiguredProperties);
         }
-        if (!healthProperties.isEmpty() || mainConfigurationProperties.hasHealthCheckConfiguration()) {
+        // need to let camel-main setup health-check using its convention over configuration
+        boolean hc = mainConfigurationProperties.health().getEnabled() != null; // health-check is enabled by default
+        if (hc || !healthProperties.isEmpty() || mainConfigurationProperties.hasHealthCheckConfiguration()) {
             LOG.debug("Auto-configuring HealthCheck from loaded properties: {}", healthProperties.size());
             setHealthCheckProperties(camelContext, healthProperties, mainConfigurationProperties.isAutoConfigurationFailFast(),
                     autoConfiguredProperties);
@@ -837,6 +848,12 @@ public abstract class BaseMainSupport extends BaseService {
             setLraCheckProperties(camelContext, lraProperties, mainConfigurationProperties.isAutoConfigurationFailFast(),
                     autoConfiguredProperties);
         }
+        if (!devConsoleProperties.isEmpty()) {
+            LOG.debug("Auto-configuring Dev Console from loaded properties: {}", devConsoleProperties.size());
+            setDevConsoleProperties(camelContext, devConsoleProperties,
+                    mainConfigurationProperties.isAutoConfigurationFailFast(),
+                    autoConfiguredProperties);
+        }
 
         // configure which requires access to the model
         MainSupportModelConfigurer.configureModelCamelContext(camelContext, mainConfigurationProperties,
@@ -1047,6 +1064,28 @@ public abstract class BaseMainSupport extends BaseService {
         }
     }
 
+    private void setDevConsoleProperties(
+            CamelContext camelContext, Map<String, Object> properties,
+            boolean failIfNotSet, Map<String, String> autoConfiguredProperties)
+            throws Exception {
+
+        // make defensive copy as we mutate the map
+        Set<String> keys = new LinkedHashSet<>(properties.keySet());
+        // set properties per console
+        for (String key : keys) {
+            String name = StringHelper.before(key, ".");
+            DevConsole console = camelContext.getExtension(DevConsoleRegistry.class).resolveById(name);
+            if (console == null) {
+                throw new IllegalArgumentException(
+                        "Cannot resolve DevConsole with id: " + name);
+            }
+            // configure all the properties on the console at once (to ensure they are configured in right order)
+            Map<String, Object> config = PropertiesHelper.extractProperties(properties, name + ".");
+            setPropertiesOnTarget(camelContext, console, config, "camel.dev-console." + name + ".", failIfNotSet, true,
+                    autoConfiguredProperties);
+        }
+    }
+
     private void bindBeansToRegistry(
             CamelContext camelContext, Map<String, Object> properties,
             String optionPrefix, boolean failIfNotSet, boolean logSummary, boolean ignoreCase,
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 db7254b..7b91343 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
@@ -29,6 +29,8 @@ import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.TypeConverters;
 import org.apache.camel.cloud.ServiceRegistry;
 import org.apache.camel.cluster.CamelClusterService;
+import org.apache.camel.console.DevConsole;
+import org.apache.camel.console.DevConsoleRegistry;
 import org.apache.camel.health.HealthCheckRegistry;
 import org.apache.camel.health.HealthCheckRepository;
 import org.apache.camel.impl.debugger.BacklogTracer;
@@ -226,6 +228,7 @@ public final class DefaultConfigurationConfigurer {
         camelContext.setMDCLoggingKeysPattern(config.getMdcLoggingKeysPattern());
         camelContext.setLoadTypeConverters(config.isLoadTypeConverters());
         camelContext.setLoadHealthChecks(config.isLoadHealthChecks());
+        camelContext.setDevConsole(config.isDevConsoleEnabled());
         if (config.isRoutesReloadEnabled()) {
             RouteWatcherReloadStrategy reloader = new RouteWatcherReloadStrategy(
                     config.getRoutesReloadDirectory(), config.isRoutesReloadDirectoryRecursive());
@@ -516,6 +519,25 @@ public final class DefaultConfigurationConfigurer {
                 }
             }
         }
+        // dev console
+        DevConsoleRegistry devConsoleRegistry = getSingleBeanOfType(registry, DevConsoleRegistry.class);
+        if (devConsoleRegistry != null) {
+            devConsoleRegistry.setCamelContext(camelContext);
+            LOG.debug("Using DevConsoleRegistry: {}", devConsoleRegistry);
+            camelContext.setExtension(DevConsoleRegistry.class, devConsoleRegistry);
+        } else {
+            // okay attempt to inject this camel context into existing dev console (if any)
+            devConsoleRegistry = DevConsoleRegistry.get(camelContext);
+            if (devConsoleRegistry != null) {
+                devConsoleRegistry.setCamelContext(camelContext);
+            }
+        }
+        if (devConsoleRegistry != null) {
+            Set<DevConsole> consoles = registry.findByType(DevConsole.class);
+            for (DevConsole console : consoles) {
+                devConsoleRegistry.register(console);
+            }
+        }
 
         // set the default thread pool profile if defined
         initThreadPoolProfiles(registry, camelContext);
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 c4caa67..be69936 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
@@ -53,6 +53,7 @@ public abstract class DefaultConfigurationProperties<T> {
     private int consumerTemplateCacheSize = 1000;
     private boolean loadTypeConverters;
     private boolean loadHealthChecks;
+    private boolean devConsoleEnabled;
     private int logDebugMaxChars;
     private boolean streamCachingEnabled;
     private String streamCachingSpoolDirectory;
@@ -379,6 +380,19 @@ public abstract class DefaultConfigurationProperties<T> {
         this.loadHealthChecks = loadHealthChecks;
     }
 
+    public boolean isDevConsoleEnabled() {
+        return devConsoleEnabled;
+    }
+
+    /**
+     * Whether to enable developer console (requires camel-console on classpath).
+     *
+     * The developer console is only for assisting during development. This is NOT for production usage.
+     */
+    public void setDevConsoleEnabled(boolean devConsoleEnabled) {
+        this.devConsoleEnabled = devConsoleEnabled;
+    }
+
     public int getLogDebugMaxChars() {
         return logDebugMaxChars;
     }
@@ -1616,6 +1630,16 @@ public abstract class DefaultConfigurationProperties<T> {
     }
 
     /**
+     * Whether to enable developer console (requires camel-console on classpath).
+     *
+     * The developer console is only for assisting during development. This is NOT for production usage.
+     */
+    public T withDevConsoleEnabled(boolean devConsoleEnabled) {
+        this.devConsoleEnabled = devConsoleEnabled;
+        return (T) this;
+    }
+
+    /**
      * Is used to limit the maximum length of the logging Camel message bodies. If the message body is longer than the
      * limit, the log message is clipped. Use -1 to have unlimited length. Use for example 1000 to log at most 1000
      * characters.
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/EventHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/EventHelper.java
index a480ee4..dd05188 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/EventHelper.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/EventHelper.java
@@ -844,7 +844,7 @@ public final class EventHelper {
         }
 
         if (((ExtendedExchange) exchange).isNotifyEvent()) {
-            // do not generate events for an notify event
+            // do not generate events for notify event
             return false;
         }
 
diff --git a/core/camel-util/src/main/java/org/apache/camel/util/UnitUtils.java b/core/camel-util/src/main/java/org/apache/camel/util/UnitUtils.java
index 528e53c..5265d71 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/UnitUtils.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/UnitUtils.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.util;
 
+import java.text.DecimalFormatSymbols;
+
 /**
  * Unit utils.
  */
@@ -31,6 +33,10 @@ public final class UnitUtils {
      * @param bytes the value in bytes
      */
     public static String printUnitFromBytes(long bytes) {
+        if (bytes < 0) {
+            return "";
+        }
+
         // http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java
         int unit = 1000;
         if (bytes < unit) {
@@ -40,4 +46,43 @@ public final class UnitUtils {
         String pre = "" + "kMGTPE".charAt(exp - 1);
         return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
     }
+
+    /**
+     * If having a size in bytes and wanting to print this in human friendly\ format with xx kB, xx MB, xx GB instead of
+     * a large byte number, using dot as decimal separator.
+     *
+     * @param bytes the value in bytes
+     */
+    public static String printUnitFromBytesDot(long bytes) {
+        return printUnitFromBytes(bytes, '.');
+    }
+
+    /**
+     * If having a size in bytes and wanting to print this in human friendly\ format with xx kB, xx MB, xx GB instead of
+     * a large byte number.
+     *
+     * @param bytes   the value in bytes
+     * @param decimal the decimal separator char to use
+     */
+    public static String printUnitFromBytes(long bytes, char decimal) {
+        if (bytes < 0) {
+            return "";
+        }
+
+        // http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java
+        int unit = 1000;
+        if (bytes < unit) {
+            return bytes + " B";
+        }
+        int exp = (int) (Math.log(bytes) / Math.log(unit));
+        String pre = "" + "kMGTPE".charAt(exp - 1);
+        String answer = String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
+
+        char sep = DecimalFormatSymbols.getInstance().getDecimalSeparator();
+        if (decimal != sep) {
+            answer = answer.replace(sep, decimal);
+        }
+        return answer;
+    }
+
 }
diff --git a/core/camel-util/src/test/java/org/apache/camel/util/UnitUtilsTest.java b/core/camel-util/src/test/java/org/apache/camel/util/UnitUtilsTest.java
index 9a73840..a932321 100644
--- a/core/camel-util/src/test/java/org/apache/camel/util/UnitUtilsTest.java
+++ b/core/camel-util/src/test/java/org/apache/camel/util/UnitUtilsTest.java
@@ -21,19 +21,20 @@ import java.text.DecimalFormatSymbols;
 import org.junit.jupiter.api.Test;
 
 import static org.apache.camel.util.UnitUtils.printUnitFromBytes;
+import static org.apache.camel.util.UnitUtils.printUnitFromBytesDot;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public class UnitUtilsTest {
 
     @Test
     public void testPrintUnitFromBytes() throws Exception {
-
         // needed for the locales that have a decimal separator other than comma
         char decimalSeparator = DecimalFormatSymbols.getInstance().getDecimalSeparator();
 
         assertEquals("999 B", printUnitFromBytes(999));
         assertEquals("1" + decimalSeparator + "0 kB", printUnitFromBytes(1000));
         assertEquals("1" + decimalSeparator + "0 kB", printUnitFromBytes(1001));
+        assertEquals("1" + decimalSeparator + "2 kB", printUnitFromBytes(1201));
 
         assertEquals("1000" + decimalSeparator + "0 kB", printUnitFromBytes(999999));
         assertEquals("1" + decimalSeparator + "0 MB", printUnitFromBytes(1000000));
@@ -41,4 +42,20 @@ public class UnitUtilsTest {
 
         assertEquals("1" + decimalSeparator + "5 MB", printUnitFromBytes(1500001));
     }
+
+    @Test
+    public void testPrintUnitFromBytesDot() throws Exception {
+        char decimalSeparator = '.';
+
+        assertEquals("999 B", printUnitFromBytes(999));
+        assertEquals("1" + decimalSeparator + "0 kB", printUnitFromBytesDot(1000));
+        assertEquals("1" + decimalSeparator + "0 kB", printUnitFromBytesDot(1001));
+        assertEquals("1" + decimalSeparator + "2 kB", printUnitFromBytesDot(1201));
+
+        assertEquals("1000" + decimalSeparator + "0 kB", printUnitFromBytesDot(999999));
+        assertEquals("1" + decimalSeparator + "0 MB", printUnitFromBytesDot(1000000));
+        assertEquals("1" + decimalSeparator + "0 MB", printUnitFromBytesDot(1000001));
+
+        assertEquals("1" + decimalSeparator + "5 MB", printUnitFromBytesDot(1500001));
+    }
 }
diff --git a/core/pom.xml b/core/pom.xml
index e56d05f..97b9c87 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -56,6 +56,7 @@
         <module>camel-core-xml</module>
         <module>camel-cloud</module>
         <module>camel-health</module>
+        <module>camel-console</module>
         <module>camel-main</module>
     </modules>
 
diff --git a/docs/user-manual/modules/ROOT/nav.adoc b/docs/user-manual/modules/ROOT/nav.adoc
index 0c9e227..88f4de7 100644
--- a/docs/user-manual/modules/ROOT/nav.adoc
+++ b/docs/user-manual/modules/ROOT/nav.adoc
@@ -4,6 +4,7 @@
 * Resources & Guides
 ** link:/community/books/[Books]
 ** xref:building.adoc[Building]
+** xref:camel-console.adoc[Camel Developer Console]
 ** xref:camel-jbang.adoc[Camel JBang]
 ** xref:camel-maven-plugin.adoc[Camel Maven Plugin]
 ** xref:camel-component-maven-plugin.adoc[Camel Component Maven Plugin]
diff --git a/docs/user-manual/modules/ROOT/pages/camel-console.adoc b/docs/user-manual/modules/ROOT/pages/camel-console.adoc
new file mode 100644
index 0000000..a551645
--- /dev/null
+++ b/docs/user-manual/modules/ROOT/pages/camel-console.adoc
@@ -0,0 +1,142 @@
+= Camel Console
+
+The `camel-console` is available from *Camel 3.15* and newer versions.
+
+The Camel Developer Console is intended assisting developers and can display
+various information about a running Camel application.
+
+Camel comes with a set of consoles out of the box from `camel-console` and `camel-catalog-console` JARs.
+These consoles can display general information about the running JVM and the OS Environment, and of course
+Camel related information such as runtime metrics of the Camel routes, and a lot more.
+
+== Using Camel Console
+
+The `camel-console` must be added to the classpath, and enabled either via
+
+[source,java]
+----
+CamelContext context = ...
+context.setDevConsole(true);
+----
+
+If using Camel Main / Spring Boot / Quarkus etc then the console can be enabled via
+configuration:
+
+[source,properties]
+----
+camel.main.dev-console-enabled = true
+----
+
+=== Dev Console and Camel JBang
+
+The Developer Console is easily available when using xref:camel-jbang.adoc[Camel JBang],
+by the `--console` argument when running JBang.
+
+For example to run a Camel route from `foo.yaml` and additional configurations from `myapp.properties` you can run as follows
+and have the console started and accessible from `http://localhost:8080/dev`
+
+[source,bash]
+----
+$ jbang CamelJBang@apache/camel run foo.yaml myapp.properties --console
+----
+
+== Writing Custom Dev Consoles
+
+To write a custom console, you need to add `camel-console` as dependency, as it comes with the
+base class `AbstractDevConsole` which we extend for our console.
+
+[source,java]
+----
+@DevConsole("foo")
+public class FooConsole extends AbstractDevConsole {
+
+    public FooConsole() {
+        super("acme", "foo", "Foolish", "A foolish console that outputs something");
+    }
+
+    @Override
+    protected Object doCall(MediaType mediaType, Map<String, Object> options) {
+        if (mediaType.TEXT.equals(mediaType)) {
+            return "Some foolish text here";
+        } else {
+            // json structure
+        }
+    }
+
+}
+----
+
+The class must be annotated with `DevConsole` and the unique id of the console (must be unique across all consoles).
+In the constructor the console specifies which group, id, display title, and description to use.
+
+The `doCall` method is responsible for gathering the information the console should output.
+
+=== Supported Media Types
+
+A console can support any of, or all of the following types:
+
+- TEXT
+- JSON
+
+The intention for `TEXT` is to be plain/text based that can be outputted in CLI and other low-level tools.
+
+For `JSON` then the intention is the console outputs a json dataset with key/value pairs that
+holds the information, which can be displayed in a custom fashion such as in a web browser, or IDE tool such as VSCode.
+
+=== Maven Configuration
+
+To make Camel able to discover custom dev consoles, then the xref:camel-component-maven-plugin.adoc[came-component-maven-plugin]
+must be used, such as:
+
+[source,xml]
+----
+<build>
+    <plugins>
+        <plugin>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-component-maven-plugin</artifactId>
+            <version>${camel-version}</version>
+            <executions>
+                <execution>
+                    <id>generate</id>
+                    <goals>
+                        <goal>generate</goal>
+                    </goals>
+                    <phase>process-classes</phase>
+                </execution>
+                <execution>
+                    <id>generate-postcompile</id>
+                    <goals>
+                        <goal>generate-postcompile</goal>
+                    </goals>
+                    <phase>prepare-package</phase>
+                </execution>
+            </executions>
+        </plugin>
+        <plugin>
+            <groupId>org.codehaus.mojo</groupId>
+            <artifactId>build-helper-maven-plugin</artifactId>
+            <executions>
+                <execution>
+                    <phase>generate-sources</phase>
+                    <goals>
+                        <goal>add-source</goal>
+                        <goal>add-resource</goal>
+                    </goals>
+                    <configuration>
+                        <sources>
+                            <source>src/generated/java</source>
+                        </sources>
+                        <resources>
+                            <resource>
+                                <directory>src/generated/resources</directory>
+                            </resource>
+                        </resources>
+                    </configuration>
+                </execution>
+            </executions>
+        </plugin>
+    </plugins>
+</build>
+----
+
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
index 2a41c91..2283fe1 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
@@ -96,6 +96,9 @@ class Run implements Callable<Integer> {
     @Option(names = { "--port" }, description = "Embeds a local HTTP server on this port")
     private int port;
 
+    @Option(names = { "--console" }, description = "Developer console at /dev on local HTTP server (port 8080 by default)")
+    private boolean console;
+
     @Override
     public Integer call() throws Exception {
         if (stopRequested) {
@@ -141,7 +144,6 @@ class Run implements Callable<Integer> {
         main.addInitialProperty("camel.main.sourceLocationEnabled", "true");
         main.addInitialProperty("camel.main.tracing", trace ? "true" : "false");
 
-        // durations
         if (maxMessages > 0) {
             main.addInitialProperty("camel.main.durationMaxMessages", String.valueOf(maxMessages));
         }
@@ -154,6 +156,9 @@ class Run implements Callable<Integer> {
         if (port > 0) {
             main.addInitialProperty("camel.jbang.platform-http.port", String.valueOf(port));
         }
+        if (console) {
+            main.addInitialProperty("camel.jbang.console", "true");
+        }
 
         if (jfr) {
             main.addInitialProperty("camel.jbang.jfr", "jfr");
diff --git a/dsl/camel-kamelet-main/pom.xml b/dsl/camel-kamelet-main/pom.xml
index fc09898..2951e77 100644
--- a/dsl/camel-kamelet-main/pom.xml
+++ b/dsl/camel-kamelet-main/pom.xml
@@ -44,6 +44,18 @@
         </dependency>
         <dependency>
             <groupId>org.apache.camel</groupId>
+            <artifactId>camel-debug</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-health</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-console</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
             <artifactId>camel-kamelet</artifactId>
         </dependency>
         <dependency>
@@ -72,6 +84,10 @@
         </dependency>
         <dependency>
             <groupId>org.apache.camel</groupId>
+            <artifactId>camel-platform-http-vertx</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
             <artifactId>camel-resourceresolver-github</artifactId>
         </dependency>
         <dependency>
@@ -86,6 +102,10 @@
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-debug</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-catalog-console</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>org.junit.jupiter</groupId>
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
index f20f4b5..8d14087 100644
--- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/KameletMain.java
@@ -167,6 +167,17 @@ public class KameletMain extends MainCommandLineSupport {
         if (port != null) {
             VertxHttpServer.registerServer(answer, Integer.parseInt(port.toString()));
         }
+        boolean console = "true".equals(getInitialProperties().get("camel.jbang.console"));
+        if (console && port == null) {
+            // use default port 8080 if console is enabled
+            VertxHttpServer.registerServer(answer, 8080);
+        }
+        if (console) {
+            // turn on developer console
+            configure().withDevConsoleEnabled(true);
+            VertxHttpServer.registerConsole(answer);
+        }
+        configure().withLoadHealthChecks(true);
 
         // need to setup jfr early
         Object jfr = getInitialProperties().get("camel.jbang.jfr");
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/VertxHttpServer.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/VertxHttpServer.java
index 6aaf136..e53cda0 100644
--- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/VertxHttpServer.java
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/VertxHttpServer.java
@@ -16,20 +16,25 @@
  */
 package org.apache.camel.main;
 
-import java.lang.reflect.Method;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import io.vertx.core.Handler;
+import io.vertx.core.http.HttpMethod;
+import io.vertx.ext.web.Route;
+import io.vertx.ext.web.RoutingContext;
 import org.apache.camel.CamelContext;
-import org.apache.camel.Component;
-import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.StartupListener;
+import org.apache.camel.component.platform.http.PlatformHttpComponent;
+import org.apache.camel.component.platform.http.vertx.VertxPlatformHttpRouter;
+import org.apache.camel.component.platform.http.vertx.VertxPlatformHttpServer;
+import org.apache.camel.component.platform.http.vertx.VertxPlatformHttpServerConfiguration;
+import org.apache.camel.console.DevConsole;
+import org.apache.camel.console.DevConsoleRegistry;
 import org.apache.camel.spi.CamelEvent;
-import org.apache.camel.support.ObjectHelper;
 import org.apache.camel.support.SimpleEventNotifierSupport;
-import org.apache.camel.util.ReflectionHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -41,6 +46,11 @@ public final class VertxHttpServer {
     private static final Logger LOG = LoggerFactory.getLogger(VertxHttpServer.class);
 
     private static final AtomicBoolean REGISTERED = new AtomicBoolean();
+    private static final AtomicBoolean CONSOLE = new AtomicBoolean();
+
+    private static VertxPlatformHttpRouter router;
+    private static VertxPlatformHttpServer server;
+    private static PlatformHttpComponent phc;
 
     private VertxHttpServer() {
     }
@@ -59,20 +69,13 @@ public final class VertxHttpServer {
 
     private static void doRegisterServer(CamelContext camelContext, int port) {
         try {
-            // must load via the classloader set on camel context that will have the classes on its classpath
-            Class<?> clazz = camelContext.getClassResolver()
-                    .resolveMandatoryClass(
-                            "org.apache.camel.component.platform.http.vertx.VertxPlatformHttpServerConfiguration");
-            Object config = clazz.getConstructors()[0].newInstance();
-            camelContext.adapt(ExtendedCamelContext.class).getBeanIntrospection()
-                    .setProperty(camelContext, config, "port", port);
-
-            clazz = camelContext.getClassResolver()
-                    .resolveMandatoryClass(
-                            "org.apache.camel.component.platform.http.vertx.VertxPlatformHttpServer");
-            Object server = clazz.getConstructors()[0].newInstance(config);
-
+            VertxPlatformHttpServerConfiguration config = new VertxPlatformHttpServerConfiguration();
+            config.setPort(port);
+            server = new VertxPlatformHttpServer(config);
             camelContext.addService(server);
+            server.start();
+            router = VertxPlatformHttpRouter.lookup(camelContext);
+            phc = camelContext.getComponent("platform-http", PlatformHttpComponent.class);
 
             // after camel is started then add event notifier
             camelContext.addStartupListener(new StartupListener() {
@@ -80,8 +83,6 @@ public final class VertxHttpServer {
                 public void onCamelContextStarted(CamelContext context, boolean alreadyStarted) throws Exception {
                     camelContext.getManagementStrategy().addEventNotifier(new SimpleEventNotifierSupport() {
 
-                        private volatile Component phc;
-                        private volatile Method method;
                         private Set<String> last;
 
                         @Override
@@ -92,11 +93,6 @@ public final class VertxHttpServer {
 
                         @Override
                         public void notify(CamelEvent event) throws Exception {
-                            if (method == null) {
-                                phc = camelContext.getComponent("platform-http", Component.class);
-                                method = ReflectionHelper.findMethod(phc.getClass(), "getHttpEndpoints");
-                            }
-
                             // when reloading then there may be more routes in the same batch, so we only want
                             // to log the summary at the end
                             if (event instanceof CamelEvent.RouteReloadedEvent) {
@@ -106,7 +102,7 @@ public final class VertxHttpServer {
                                 }
                             }
 
-                            Set<String> endpoints = (Set<String>) ObjectHelper.invokeMethodSafe(method, phc);
+                            Set<String> endpoints = phc.getHttpEndpoints();
                             if (endpoints.isEmpty()) {
                                 return;
                             }
@@ -131,4 +127,45 @@ public final class VertxHttpServer {
         }
     }
 
+    public static void registerConsole(CamelContext camelContext) {
+        if (CONSOLE.compareAndSet(false, true)) {
+            doRegisterConsole(camelContext);
+        }
+    }
+
+    private static void doRegisterConsole(CamelContext context) {
+        Route dev = router.route("/dev");
+        dev.method(HttpMethod.GET);
+        dev.handler(router.bodyHandler());
+        dev.produces("text/plain");
+        dev.handler(new Handler<RoutingContext>() {
+            @Override
+            public void handle(RoutingContext ctx) {
+                DevConsoleRegistry dcr = context.getExtension(DevConsoleRegistry.class);
+                if (dcr != null && dcr.isEnabled()) {
+                    StringBuilder sb = new StringBuilder();
+                    dcr.stream().forEach(c -> {
+                        if (c.supportMediaType(DevConsole.MediaType.TEXT)) {
+                            String text = (String) c.call(DevConsole.MediaType.TEXT);
+                            if (text != null) {
+                                sb.append(c.getDisplayName()).append(":");
+                                sb.append("\n\n");
+                                sb.append(text);
+                                sb.append("\n\n");
+                            }
+                        }
+                    });
+                    if (sb.length() > 0) {
+                        ctx.end(sb.toString());
+                    } else {
+                        ctx.end("Developer Console is not enabled");
+                    }
+                } else {
+                    ctx.end("Developer Console is not enabled");
+                }
+            }
+        });
+        phc.addHttpEndpoint("/dev");
+    }
+
 }
diff --git a/parent/pom.xml b/parent/pom.xml
index 36a1a66..4a59e92 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -730,6 +730,11 @@
             </dependency>
             <dependency>
                 <groupId>org.apache.camel</groupId>
+                <artifactId>camel-console</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.camel</groupId>
                 <artifactId>camel-main</artifactId>
                 <version>${project.version}</version>
             </dependency>
@@ -2874,6 +2879,11 @@
             </dependency>
             <dependency>
                 <groupId>org.apache.camel</groupId>
+                <artifactId>camel-catalog-console</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.camel</groupId>
                 <artifactId>camel-catalog-lucene</artifactId>
                 <version>${project.version}</version>
             </dependency>
diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/event/StepCompletedEvent.java b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/annotations/DevConsole.java
similarity index 61%
copy from core/camel-base/src/main/java/org/apache/camel/impl/event/StepCompletedEvent.java
copy to tooling/spi-annotations/src/main/java/org/apache/camel/spi/annotations/DevConsole.java
index d90b195..f504ea0 100644
--- a/core/camel-base/src/main/java/org/apache/camel/impl/event/StepCompletedEvent.java
+++ b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/annotations/DevConsole.java
@@ -14,22 +14,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.impl.event;
+package org.apache.camel.spi.annotations;
 
-import org.apache.camel.Exchange;
-import org.apache.camel.spi.CamelEvent;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
 /**
- * Event after a step has been completed successfully.
+ * Marks a class as a custom developer console.
  */
-public class StepCompletedEvent extends AbstractStepEvent implements CamelEvent.StepCompletedEvent {
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Target({ ElementType.TYPE })
+@ServiceFactory("dev-console")
+public @interface DevConsole {
 
-    public StepCompletedEvent(Exchange source, String stepId) {
-        super(source, stepId);
-    }
+    /**
+     * The ID of the dev console.
+     */
+    String value();
 
-    @Override
-    public String toString() {
-        return "Step[" + getStepId() + "] completed";
-    }
 }