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
The following commit(s) were added to refs/heads/main by this push:
new f67bc954fa4 supervising route controller (#12463)
f67bc954fa4 is described below
commit f67bc954fa4130831a29a3e8db32bd6997994ff6
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun Dec 17 07:28:14 2023 +0100
supervising route controller (#12463)
* CAMEL-20242: camel-core: RouteController health check to be DOWN during
starting routes. Supervising route controller option to be DOWN during
restarting phase.
* CAMEL-20242: camel-main - Health check should not be as verbose by
default.
---
.../main/camel-main-configuration-metadata.json | 1 +
.../apache/camel/catalog/schemas/camel-spring.xsd | 11 ++
.../platform/http/main/MainHttpServer.java | 55 +++++----
.../java/org/apache/camel/spi/RouteController.java | 7 ++
.../camel/spi/SupervisingRouteController.java | 18 +++
.../camel/impl/engine/DefaultRouteController.java | 5 +
.../engine/DefaultSupervisingRouteController.java | 50 +++++++-
.../camel/impl/engine/InternalRouteController.java | 5 +
.../camel/impl/console/HealthDevConsole.java | 20 ++--
.../camel/impl/console/RouteControllerConsole.java | 6 +
.../org/apache/camel/core/xml/routeController.json | 3 +-
.../core/xml/AbstractCamelContextFactoryBean.java | 8 +-
.../core/xml/CamelRouteControllerDefinition.java | 17 +++
.../camel/health-check/route-controller-check | 2 +
.../impl/health/RouteControllerHealthCheck.java | 131 +++++++++++++++++++++
.../MainConfigurationPropertiesConfigurer.java | 6 +
.../camel-main-configuration-metadata.json | 1 +
core/camel-main/src/main/docs/main.adoc | 3 +-
.../camel/main/DefaultConfigurationConfigurer.java | 1 +
.../camel/main/DefaultConfigurationProperties.java | 30 +++++
.../org/apache/camel/util/backoff/BackOff.java | 8 +-
.../modules/ROOT/pages/route-controller.adoc | 2 +
.../commands/action/RouteControllerAction.java | 22 ++--
23 files changed, 369 insertions(+), 43 deletions(-)
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 5577ce62cb1..1bf93cf0c12 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
@@ -93,6 +93,7 @@
{ "name": "camel.main.routeControllerSuperviseEnabled", "description": "To
enable using supervising route controller which allows Camel to startup and
then the controller takes care of starting the routes in a safe manner. This
can be used when you want to startup Camel despite a route may otherwise fail
fast during startup and cause Camel to fail to startup as well. By delegating
the route startup to the supervising route controller then its manages the
startup using a background th [...]
{ "name": "camel.main.routeControllerThreadPoolSize", "description": "The
number of threads used by the route controller scheduled thread pool that are
used for restarting routes. The pool uses 1 thread by default, but you can
increase this to allow the controller to concurrently attempt to restart
multiple routes in case more than one route has problems starting.",
"sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type":
"integer", "javaType": "int" },
{ "name": "camel.main.routeControllerUnhealthyOnExhausted", "description":
"Whether to mark the route as unhealthy (down) when all restarting attempts
(backoff) have failed and the route is not successfully started and the route
manager is giving up. Setting this to true allows health checks to know about
this and can report the Camel application as DOWN. The default is false.",
"sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type":
"boolean", "javaType": "bool [...]
+ { "name": "camel.main.routeControllerUnhealthyOnRestarting",
"description": "Whether to mark the route as unhealthy (down) when the route
failed to initially start, and is being controlled for restarting (backoff).
Setting this to true allows health checks to know about this and can report the
Camel application as DOWN. The default is false.", "sourceType":
"org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean",
"javaType": "boolean", "defaultValue": "false" },
{ "name": "camel.main.routeFilterExcludePattern", "description": "Used for
filtering routes routes matching the given pattern, which follows the following
rules: - Match by route id - Match by route input endpoint uri The matching is
using exact match, by wildcard and regular expression as documented by
PatternHelper#matchPattern(String,String) . For example to only include routes
which starts with foo in their route id's, use: include=foo* And to exclude
routes which starts from [...]
{ "name": "camel.main.routeFilterIncludePattern", "description": "Used for
filtering routes matching the given pattern, which follows the following rules:
- Match by route id - Match by route input endpoint uri The matching is using
exact match, by wildcard and regular expression as documented by
PatternHelper#matchPattern(String,String) . For example to only include routes
which starts with foo in their route id's, use: include=foo* And to exclude
routes which starts from JMS en [...]
{ "name": "camel.main.routesBuilderClasses", "description": "Sets classes
names that implement RoutesBuilder .", "sourceType":
"org.apache.camel.main.MainConfigurationProperties", "type": "string",
"javaType": "java.lang.String" },
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 9141b19db6a..263a46d5a4f 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
@@ -3058,6 +3058,17 @@ value: 1.0
Whether to mark the route as unhealthy (down) when all restarting attempts
(backoff) have failed and the route is not
successfully started and the route manager is giving up. Setting this to true
allows health checks to know about this
and can report the Camel application as DOWN. Default value: false
+]]>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="unhealthyOnRestarting" type="xs:string">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+<![CDATA[
+Whether to mark the route as unhealthy (down) when the route failed to
initially start, and is being controlled for
+restarting (backoff). Setting this to true allows health checks to know about
this and can report the Camel application
+as DOWN. Default value: false
]]>
</xs:documentation>
</xs:annotation>
diff --git
a/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/MainHttpServer.java
b/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/MainHttpServer.java
index 32e1ff68f8e..840eb824bd0 100644
---
a/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/MainHttpServer.java
+++
b/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/MainHttpServer.java
@@ -323,28 +323,30 @@ public class MainHttpServer extends ServiceSupport
implements CamelContextAware,
public void handle(RoutingContext ctx) {
ctx.response().putHeader("content-type", "application/json");
+ HealthCheckRegistry registry =
HealthCheckRegistry.get(camelContext);
+ String level = ctx.request().getParam("exposureLevel");
+ if (level == null) {
+ level = registry.getExposureLevel();
+ }
+ String includeStackTrace =
ctx.request().getParam("stackTrace");
+ String includeData = ctx.request().getParam("data");
+
boolean all = ctx.currentRoute() == health;
boolean liv = ctx.currentRoute() == live;
boolean rdy = ctx.currentRoute() == ready;
Collection<HealthCheck.Result> res;
if (all) {
- res = HealthCheckHelper.invoke(camelContext);
+ res = HealthCheckHelper.invoke(camelContext, level);
} else if (liv) {
- res = HealthCheckHelper.invokeLiveness(camelContext);
+ res = HealthCheckHelper.invokeLiveness(camelContext,
level);
} else {
- res = HealthCheckHelper.invokeReadiness(camelContext);
+ res = HealthCheckHelper.invokeReadiness(camelContext,
level);
}
StringBuilder sb = new StringBuilder();
sb.append("{\n");
- HealthCheckRegistry registry =
HealthCheckRegistry.get(camelContext);
- String level = ctx.request().getParam("exposureLevel");
- if (level == null) {
- level = registry.getExposureLevel();
- }
-
// are we UP
boolean up = HealthCheckHelper.isResultsUp(res, rdy);
@@ -354,12 +356,12 @@ public class MainHttpServer extends ServiceSupport
implements CamelContextAware,
} else if ("full".equals(level)) {
// include all details
List<HealthCheck.Result> list = new ArrayList<>(res);
- healthCheckDetails(sb, list, up);
+ healthCheckDetails(sb, list, up, level, includeStackTrace,
includeData);
} else {
// include only DOWN details
List<HealthCheck.Result> downs = res.stream().filter(r ->
r.getState().equals(HealthCheck.State.DOWN))
.collect(Collectors.toList());
- healthCheckDetails(sb, downs, up);
+ healthCheckDetails(sb, downs, up, level,
includeStackTrace, includeData);
}
sb.append("}\n");
@@ -391,7 +393,9 @@ public class MainHttpServer extends ServiceSupport
implements CamelContextAware,
}
}
- private static void healthCheckDetails(StringBuilder sb,
List<HealthCheck.Result> checks, boolean up) {
+ private static void healthCheckDetails(
+ StringBuilder sb, List<HealthCheck.Result> checks, boolean up,
String level, String includeStackTrace,
+ String includeData) {
healthCheckStatus(sb, up);
if (!checks.isEmpty()) {
@@ -400,7 +404,7 @@ public class MainHttpServer extends ServiceSupport
implements CamelContextAware,
for (int i = 0; i < checks.size(); i++) {
HealthCheck.Result d = checks.get(i);
sb.append(" {\n");
- reportHealthCheck(sb, d);
+ reportHealthCheck(sb, d, level, includeStackTrace,
includeData);
if (i < checks.size() - 1) {
sb.append(" },\n");
} else {
@@ -411,20 +415,29 @@ public class MainHttpServer extends ServiceSupport
implements CamelContextAware,
}
}
- private static void reportHealthCheck(StringBuilder sb, HealthCheck.Result
d) {
+ private static void reportHealthCheck(
+ StringBuilder sb, HealthCheck.Result d, String level, String
includeStackTrace, String includeData) {
sb.append(" \"name\":
\"").append(d.getCheck().getId()).append("\",\n");
- sb.append(" \"status\":
\"").append(d.getState()).append("\",\n");
- if (d.getError().isPresent()) {
+ sb.append(" \"status\":
\"").append(d.getState()).append("\"");
+ if (("full".equals(level) || "true".equals(includeStackTrace)) &&
d.getError().isPresent()) {
+ // include error message in full exposure
+ sb.append(",\n");
String msg = allCausedByErrorMessages(d.getError().get());
sb.append(" \"error-message\": \"").append(msg)
- .append("\",\n");
- sb.append(" \"error-stacktrace\":
\"").append(errorStackTrace(d.getError().get()))
- .append("\",\n");
+ .append("\"");
+ if ("true".equals(includeStackTrace)) {
+ sb.append(",\n");
+ sb.append(" \"error-stacktrace\":
\"").append(errorStackTrace(d.getError().get()))
+ .append("\"");
+ }
}
if (d.getMessage().isPresent()) {
- sb.append(" \"message\":
\"").append(d.getMessage().get()).append("\",\n");
+ sb.append(",\n");
+ sb.append(" \"message\":
\"").append(d.getMessage().get()).append("\"");
}
- if (d.getDetails() != null && !d.getDetails().isEmpty()) {
+ // only include data if was enabled
+ if (("true".equals(includeData)) && d.getDetails() != null &&
!d.getDetails().isEmpty()) {
+ sb.append(",\n");
// lets use sorted keys
Iterator<String> it = new
TreeSet<>(d.getDetails().keySet()).iterator();
sb.append(" \"data\": {\n");
diff --git
a/core/camel-api/src/main/java/org/apache/camel/spi/RouteController.java
b/core/camel-api/src/main/java/org/apache/camel/spi/RouteController.java
index 5b7eefcff34..7aace75c0f6 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/RouteController.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/RouteController.java
@@ -99,6 +99,13 @@ public interface RouteController extends CamelContextAware,
StaticService {
*/
boolean isStartingRoutes();
+ /**
+ * Indicates if the route controller has routes that are unhealthy such as
they have not yet been successfully
+ * started, and if being supervised then the route can either be pending
restarts or failed all restart attempts and
+ * are exhausted.
+ */
+ boolean isUnhealthyRoutes();
+
/**
* Reloads all the routes
*
diff --git
a/core/camel-api/src/main/java/org/apache/camel/spi/SupervisingRouteController.java
b/core/camel-api/src/main/java/org/apache/camel/spi/SupervisingRouteController.java
index dabd4edf1c8..f80e8248bda 100644
---
a/core/camel-api/src/main/java/org/apache/camel/spi/SupervisingRouteController.java
+++
b/core/camel-api/src/main/java/org/apache/camel/spi/SupervisingRouteController.java
@@ -128,6 +128,18 @@ public interface SupervisingRouteController extends
RouteController {
*/
boolean isUnhealthyOnExhausted();
+ boolean isUnhealthyOnRestarting();
+
+ /**
+ * Whether to mark the route as unhealthy (down) when the route failed to
initially start, and is being controlled
+ * for restarting (backoff).
+ *
+ * Setting this to true allows health checks to know about this and can
report the Camel application as DOWN.
+ *
+ * The default is false.
+ */
+ void setUnhealthyOnRestarting(boolean unhealthyOnRestarting);
+
/**
* Return the list of routes that are currently under restarting by this
controller.
*
@@ -161,4 +173,10 @@ public interface SupervisingRouteController extends
RouteController {
*/
Throwable getRestartException(String routeId);
+ /**
+ * Whether the route controller is currently starting routes for the first
time. This only reports on the first time
+ * start phase.
+ */
+ boolean isStartingRoutes();
+
}
diff --git
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultRouteController.java
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultRouteController.java
index 54ee46708f4..fa63220eedb 100644
---
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultRouteController.java
+++
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultRouteController.java
@@ -108,6 +108,11 @@ public class DefaultRouteController extends ServiceSupport
implements RouteContr
return getInternalRouteController().isStartingRoutes();
}
+ @Override
+ public boolean isUnhealthyRoutes() {
+ return getInternalRouteController().isUnhealthyRoutes();
+ }
+
@Override
public void reloadAllRoutes() throws Exception {
getInternalRouteController().reloadAllRoutes();
diff --git
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultSupervisingRouteController.java
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultSupervisingRouteController.java
index 2f0e72ed0e5..5df8119b4fc 100644
---
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultSupervisingRouteController.java
+++
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultSupervisingRouteController.java
@@ -75,6 +75,7 @@ public class DefaultSupervisingRouteController extends
DefaultRouteController im
private final Set<String> nonSupervisedRoutes;
private final RouteManager routeManager;
private volatile CamelContextStartupListener listener;
+ private volatile boolean startingRoutes = true; // state during starting
routes on bootstrap
private volatile BackOffTimer timer;
private volatile ScheduledExecutorService executorService;
private volatile BackOff backOff;
@@ -88,6 +89,7 @@ public class DefaultSupervisingRouteController extends
DefaultRouteController im
private long backOffMaxAttempts;
private double backOffMultiplier = 1.0d;
private boolean unhealthyOnExhausted;
+ private boolean unhealthyOnRestarting;
public DefaultSupervisingRouteController() {
this.lock = new Object();
@@ -182,6 +184,14 @@ public class DefaultSupervisingRouteController extends
DefaultRouteController im
this.unhealthyOnExhausted = unhealthyOnExhausted;
}
+ public boolean isUnhealthyOnRestarting() {
+ return unhealthyOnRestarting;
+ }
+
+ public void setUnhealthyOnRestarting(boolean unhealthyOnRestarting) {
+ this.unhealthyOnRestarting = unhealthyOnRestarting;
+ }
+
protected BackOff getBackOff(String id) {
// currently all routes use the same backoff
return backOff;
@@ -237,6 +247,28 @@ public class DefaultSupervisingRouteController extends
DefaultRouteController im
// Route management
// *********************************
+ @Override
+ public boolean isUnhealthyRoutes() {
+ boolean answer = startingRoutes;
+
+ // if we have started the routes first time, but some failed and are
scheduled for restart
+ // then we may report as still starting routes if we should be
unhealthy on restarting
+ if (!answer && isUnhealthyOnRestarting()) {
+ // mark as still starting routes if we have routes to restart
+ answer = !routeManager.routes.isEmpty();
+ }
+ if (!answer && isUnhealthyOnExhausted()) {
+ // mark as still starting routes if we have exhausted routes that
should be unhealthy
+ answer = !routeManager.exhausted.isEmpty();
+ }
+ return answer;
+ }
+
+ @Override
+ public boolean isStartingRoutes() {
+ return startingRoutes;
+ }
+
@Override
public void startRoute(String routeId) throws Exception {
final Optional<RouteHolder> route = routes.stream().filter(r ->
r.getId().equals(routeId)).findFirst();
@@ -457,6 +489,14 @@ public class DefaultSupervisingRouteController extends
DefaultRouteController im
}
private void startSupervisedRoutes() {
+ try {
+ doStartSupervisedRoutes();
+ } finally {
+ startingRoutes = false;
+ }
+ }
+
+ private void doStartSupervisedRoutes() {
if (!isRunAllowed()) {
return;
}
@@ -482,7 +522,7 @@ public class DefaultSupervisingRouteController extends
DefaultRouteController im
if (getCamelContext().getStartupSummaryLevel() !=
StartupSummaryLevel.Off
&& getCamelContext().getStartupSummaryLevel() !=
StartupSummaryLevel.Oneline) {
- // log after first round of attempts
+ // log after first round of attempts (some routes may be scheduled
for restart)
logRouteStartupSummary();
}
}
@@ -595,6 +635,14 @@ public class DefaultSupervisingRouteController extends
DefaultRouteController im
BackOffTimer.Task task = timer.schedule(backOff,
context -> {
final BackOffTimer.Task state =
getBackOffContext(r.getId()).orElse(null);
long attempt = state != null ?
state.getCurrentAttempts() : 0;
+
+ if (!getCamelContext().isRunAllowed()) {
+ // Camel is shutting down so do not attempt to
start route
+ logger.info("Restarting route: {} attempt: {}
is cancelled due CamelContext is shutting down",
+ r.getId(), attempt);
+ return true;
+ }
+
try {
logger.info("Restarting route: {} attempt:
{}", r.getId(), attempt);
doStartRoute(r, false, rx ->
DefaultSupervisingRouteController.super.startRoute(rx.getId()));
diff --git
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/InternalRouteController.java
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/InternalRouteController.java
index 4341050bbe8..a3e49591e8e 100644
---
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/InternalRouteController.java
+++
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/InternalRouteController.java
@@ -111,6 +111,11 @@ class InternalRouteController implements RouteController {
return abstractCamelContext.isStartingRoutes();
}
+ @Override
+ public boolean isUnhealthyRoutes() {
+ return false;
+ }
+
@Override
public ServiceStatus getRouteStatus(String routeId) {
return abstractCamelContext.getRouteStatus(routeId);
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
index 175849de7c2..75aca65610a 100644
---
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
@@ -53,17 +53,21 @@ public class HealthDevConsole extends AbstractDevConsole {
sb.append(String.format("\n %s: %s",
res.getCheck().getId(), res.getState()));
} else {
if (res.getMessage().isPresent()) {
- sb.append(String.format("\n %s: %s (%s)",
res.getCheck().getId(), res.getState(), res.getMessage()));
+ sb.append(
+ String.format("\n %s: %s (%s)",
res.getCheck().getId(), res.getState(), res.getMessage().get()));
} else {
sb.append(String.format("\n %s: %s",
res.getCheck().getId(), res.getState()));
}
- 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");
+ if ("full".equals(exposureLevel)) {
+ if (res.getError().isPresent()) {
+ Throwable cause = res.getError().get();
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ cause.printStackTrace(pw);
+ sb.append("\n\n");
+ sb.append(sw);
+ sb.append("\n\n");
+ }
}
}
});
diff --git
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteControllerConsole.java
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteControllerConsole.java
index cacfc0c8f86..bb64f241393 100644
---
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteControllerConsole.java
+++
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteControllerConsole.java
@@ -66,6 +66,8 @@ public class RouteControllerConsole extends
AbstractDevConsole {
long started = routes.stream().filter(r ->
src.getRouteStatus(r.getRouteId()).isStarted())
.count();
+ sb.append(String.format("\nInitial Starting Routes: %b",
src.isStartingRoutes()));
+ sb.append(String.format("\nUnhealthy Routes: %b",
src.isUnhealthyRoutes()));
sb.append(String.format("Total Routes: %d", routes.size()));
sb.append(String.format("\nStarted Routes: %d", started));
sb.append(String.format("\nRestarting Routes: %d",
src.getRestartingRoutes().size()));
@@ -76,6 +78,7 @@ public class RouteControllerConsole extends
AbstractDevConsole {
sb.append(String.format("\nBackoff Max Elapsed Time: %d",
src.getBackOffMaxElapsedTime()));
sb.append(String.format("\nBackoff Max Attempts: %d",
src.getBackOffMaxAttempts()));
sb.append(String.format("\nThread Pool Size: %d",
src.getThreadPoolSize()));
+ sb.append(String.format("\nUnhealthy On Restarting: %b",
src.isUnhealthyOnRestarting()));
sb.append(String.format("\nUnhealthy On Exhaust: %b",
src.isUnhealthyOnExhausted()));
sb.append("\n\nRoutes:\n");
@@ -177,6 +180,8 @@ public class RouteControllerConsole extends
AbstractDevConsole {
.count();
root.put("controller", "SupervisingRouteController");
+ root.put("startingRoutes", src.isStartingRoutes());
+ root.put("unhealthyRoutes", src.isUnhealthyRoutes());
root.put("totalRoutes", routes.size());
root.put("startedRoutes", started);
root.put("restartingRoutes", src.getRestartingRoutes().size());
@@ -187,6 +192,7 @@ public class RouteControllerConsole extends
AbstractDevConsole {
root.put("backoffMaxElapsedTime", src.getBackOffMaxElapsedTime());
root.put("backoffMaxAttempts", src.getBackOffMaxAttempts());
root.put("threadPoolSize", src.getThreadPoolSize());
+ root.put("unhealthyOnRestarting", src.isUnhealthyOnRestarting());
root.put("unhealthyOnExhausted", src.isUnhealthyOnExhausted());
root.put("routes", list);
diff --git
a/core/camel-core-xml/src/generated/resources/org/apache/camel/core/xml/routeController.json
b/core/camel-core-xml/src/generated/resources/org/apache/camel/core/xml/routeController.json
index e10475e45b3..241a57d2de1 100644
---
a/core/camel-core-xml/src/generated/resources/org/apache/camel/core/xml/routeController.json
+++
b/core/camel-core-xml/src/generated/resources/org/apache/camel/core/xml/routeController.json
@@ -24,6 +24,7 @@
"backOffMaxAttempts": { "index": 9, "kind": "attribute", "displayName":
"Back Off Max Attempts", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Backoff maximum number of attempts to restart a route that
failed to startup. When this threshold has been exceeded then the controller
will give up attempting to restart the route, and the route will remain as
stopped." },
"backOffMultiplier": { "index": 10, "kind": "attribute", "displayName":
"Back Off Multiplier", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "1.0", "description": "Backoff multiplier to use for
exponential backoff. This is used to extend the delay between restart
attempts." },
"unhealthyOnExhausted": { "index": 11, "kind": "attribute", "displayName":
"Unhealthy On Exhausted", "required": false, "type": "boolean", "javaType":
"java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": false, "description": "Whether to mark the route as unhealthy
(down) when all restarting attempts (backoff) have failed and the route is not
successfully started and the route manager is giving up. Setting this to true
allows health checks to [...]
- "loggingLevel": { "index": 12, "kind": "attribute", "displayName":
"Logging Level", "required": false, "type": "enum", "javaType":
"org.apache.camel.LoggingLevel", "enum": [ "TRACE", "DEBUG", "INFO", "WARN",
"ERROR", "OFF" ], "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "DEBUG", "description": "Sets the logging level used for
logging route activity (such as starting and stopping routes). The default
logging level is DEBUG." }
+ "unhealthyOnRestarting": { "index": 12, "kind": "attribute",
"displayName": "Unhealthy On Restarting", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": false, "description": "Whether to mark the
route as unhealthy (down) when the route failed to initially start, and is
being controlled for restarting (backoff). Setting this to true allows health
checks to know about this and can report the [...]
+ "loggingLevel": { "index": 13, "kind": "attribute", "displayName":
"Logging Level", "required": false, "type": "enum", "javaType":
"org.apache.camel.LoggingLevel", "enum": [ "TRACE", "DEBUG", "INFO", "WARN",
"ERROR", "OFF" ], "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "DEBUG", "description": "Sets the logging level used for
logging route activity (such as starting and stopping routes). The default
logging level is DEBUG." }
}
}
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 9e4f9db6442..316ccf72302 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
@@ -880,11 +880,15 @@ public abstract class AbstractCamelContextFactoryBean<T
extends ModelCamelContex
src.setBackOffMultiplier(backOffMultiplier);
}
Boolean unhealthyOnExhausted =
CamelContextHelper.parseBoolean(getContext(), rc.getUnhealthyOnExhausted());
- if (unhealthyOnExhausted != null && unhealthyOnExhausted) {
+ if (src != null && unhealthyOnExhausted != null &&
unhealthyOnExhausted) {
src.setUnhealthyOnExhausted(unhealthyOnExhausted);
}
+ Boolean unhealthyOnRestarting =
CamelContextHelper.parseBoolean(getContext(), rc.getUnhealthyOnRestarting());
+ if (src != null && unhealthyOnRestarting != null &&
unhealthyOnRestarting) {
+ src.setUnhealthyOnRestarting(unhealthyOnRestarting);
+ }
LoggingLevel loggingLevel = CamelContextHelper.parse(getContext(),
LoggingLevel.class, rc.getLoggingLevel());
- if (loggingLevel != null) {
+ if (src != null && loggingLevel != null) {
src.setLoggingLevel(loggingLevel);
}
}
diff --git
a/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/CamelRouteControllerDefinition.java
b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/CamelRouteControllerDefinition.java
index 3e3ec752627..fa09fc2ccbf 100644
---
a/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/CamelRouteControllerDefinition.java
+++
b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/CamelRouteControllerDefinition.java
@@ -60,6 +60,9 @@ public class CamelRouteControllerDefinition extends
IdentifiedType {
@Metadata(defaultValue = "false", javaType = "java.lang.Boolean")
private String unhealthyOnExhausted;
@XmlAttribute
+ @Metadata(defaultValue = "false", javaType = "java.lang.Boolean")
+ private String unhealthyOnRestarting;
+ @XmlAttribute
@Metadata(javaType = "org.apache.camel.LoggingLevel", defaultValue =
"DEBUG", enums = "TRACE,DEBUG,INFO,WARN,ERROR,OFF")
private String loggingLevel;
@@ -209,6 +212,20 @@ public class CamelRouteControllerDefinition extends
IdentifiedType {
this.unhealthyOnExhausted = unhealthyOnExhausted;
}
+ public String getUnhealthyOnRestarting() {
+ return unhealthyOnRestarting;
+ }
+
+ /**
+ * Whether to mark the route as unhealthy (down) when the route failed to
initially start, and is being controlled
+ * for restarting (backoff).
+ *
+ * Setting this to true allows health checks to know about this and can
report the Camel application as DOWN.
+ */
+ public void setUnhealthyOnRestarting(String unhealthyOnRestarting) {
+ this.unhealthyOnRestarting = unhealthyOnRestarting;
+ }
+
public String getLoggingLevel() {
return loggingLevel;
}
diff --git
a/core/camel-health/src/generated/resources/META-INF/services/org/apache/camel/health-check/route-controller-check
b/core/camel-health/src/generated/resources/META-INF/services/org/apache/camel/health-check/route-controller-check
new file mode 100644
index 00000000000..d6acdd88860
--- /dev/null
+++
b/core/camel-health/src/generated/resources/META-INF/services/org/apache/camel/health-check/route-controller-check
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.health.RouteControllerHealthCheck
diff --git
a/core/camel-health/src/main/java/org/apache/camel/impl/health/RouteControllerHealthCheck.java
b/core/camel-health/src/main/java/org/apache/camel/impl/health/RouteControllerHealthCheck.java
new file mode 100644
index 00000000000..5f23eb6e6a1
--- /dev/null
+++
b/core/camel-health/src/main/java/org/apache/camel/impl/health/RouteControllerHealthCheck.java
@@ -0,0 +1,131 @@
+/*
+ * 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.health;
+
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.camel.Ordered;
+import org.apache.camel.Route;
+import org.apache.camel.health.HealthCheckResultBuilder;
+import org.apache.camel.spi.RouteController;
+import org.apache.camel.spi.SupervisingRouteController;
+import org.apache.camel.util.URISupport;
+import org.apache.camel.util.backoff.BackOffTimer;
+
+/**
+ * Readiness {@link org.apache.camel.health.HealthCheck} for route controller.
+ */
[email protected]("route-controller-check")
+public class RouteControllerHealthCheck extends AbstractHealthCheck {
+
+ public RouteControllerHealthCheck() {
+ super("camel", "route-controller");
+ }
+
+ @Override
+ public int getOrder() {
+ // controller should be early
+ return Ordered.HIGHEST + 1000;
+ }
+
+ @Override
+ protected void doCall(HealthCheckResultBuilder builder, Map<String,
Object> options) {
+ boolean up = false;
+
+ RouteController rc = getCamelContext().getRouteController();
+ if (rc != null) {
+ // should only be up if there are no unhealthy routes
+ up = !rc.isUnhealthyRoutes();
+ // do we have any details about why we are not up
+ if (!up && rc instanceof SupervisingRouteController src) {
+ Set<Route> routes = new
TreeSet<>(Comparator.comparing(Route::getId));
+ routes.addAll(src.getRestartingRoutes());
+ for (Route route : routes) {
+ builderDetails(builder, src, route, false);
+ }
+ routes = new TreeSet<>(Comparator.comparing(Route::getId));
+ routes.addAll(src.getExhaustedRoutes());
+ for (Route route : routes) {
+ builderDetails(builder, src, route, true);
+ }
+ }
+ }
+
+ if (up) {
+ builder.up();
+ } else {
+ builder.detail("route.controller", "Starting routes in progress");
+ builder.down();
+ }
+ }
+
+ private void builderDetails(
+ HealthCheckResultBuilder builder, SupervisingRouteController src,
Route route, boolean exhausted) {
+ String routeId = route.getRouteId();
+ Throwable cause = src.getRestartException(routeId);
+ if (cause != null) {
+ String status = src.getRouteStatus(routeId).name();
+ String uri = route.getEndpoint().getEndpointBaseUri();
+ uri = URISupport.sanitizeUri(uri);
+
+ BackOffTimer.Task state = src.getRestartingRouteState(routeId);
+ long attempts = state != null ? state.getCurrentAttempts() : 0;
+ long elapsed;
+ long last;
+ long next;
+ // we can only track elapsed/time for active supervised routes
+ elapsed = state != null && BackOffTimer.Task.Status.Active ==
state.getStatus()
+ ? state.getCurrentElapsedTime() : 0;
+ last = state != null && BackOffTimer.Task.Status.Active ==
state.getStatus()
+ ? state.getLastAttemptTime() : 0;
+ next = state != null && BackOffTimer.Task.Status.Active ==
state.getStatus()
+ ? state.getNextAttemptTime() : 0;
+
+ String key = "route." + routeId;
+ builder.detail(key + ".id", routeId);
+ builder.detail(key + ".status", status);
+ builder.detail(key + ".phase", exhausted ? "Exhausted" :
"Restarting");
+ builder.detail(key + ".uri", uri);
+ builder.detail(key + ".attempts", attempts);
+ builder.detail(key + ".lastAttempt", last);
+ builder.detail(key + ".nextAttempt", next);
+ builder.detail(key + ".elapsed", elapsed);
+ if (cause.getMessage() != null) {
+ builder.detail(key + ".error", cause.getMessage());
+ // only one exception can be stored so lets just store first
found
+ if (builder.error() == null) {
+ builder.error(cause);
+ String msg;
+ if (exhausted) {
+ msg = String.format("Restarting route: %s is exhausted
after %s attempts due %s."
+ + " No more attempts will be made
and the route is no longer supervised by this route controller and remains as
stopped.",
+ routeId, attempts,
+ cause.getMessage());
+ } else {
+ msg = String.format("Failed restarting route: %s
attempt: %s due: %s", routeId, attempts,
+ cause.getMessage());
+ }
+ builder.message(msg);
+ }
+ }
+ }
+ }
+
+}
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 fab74363ba5..35c2a6e7c7a 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
@@ -173,6 +173,8 @@ public class MainConfigurationPropertiesConfigurer extends
org.apache.camel.supp
case "RouteControllerThreadPoolSize":
target.setRouteControllerThreadPoolSize(property(camelContext, int.class,
value)); return true;
case "routecontrollerunhealthyonexhausted":
case "RouteControllerUnhealthyOnExhausted":
target.setRouteControllerUnhealthyOnExhausted(property(camelContext,
boolean.class, value)); return true;
+ case "routecontrollerunhealthyonrestarting":
+ case "RouteControllerUnhealthyOnRestarting":
target.setRouteControllerUnhealthyOnRestarting(property(camelContext,
boolean.class, value)); return true;
case "routefilterexcludepattern":
case "RouteFilterExcludePattern":
target.setRouteFilterExcludePattern(property(camelContext,
java.lang.String.class, value)); return true;
case "routefilterincludepattern":
@@ -434,6 +436,8 @@ public class MainConfigurationPropertiesConfigurer extends
org.apache.camel.supp
case "RouteControllerThreadPoolSize": return int.class;
case "routecontrollerunhealthyonexhausted":
case "RouteControllerUnhealthyOnExhausted": return boolean.class;
+ case "routecontrollerunhealthyonrestarting":
+ case "RouteControllerUnhealthyOnRestarting": return boolean.class;
case "routefilterexcludepattern":
case "RouteFilterExcludePattern": return java.lang.String.class;
case "routefilterincludepattern":
@@ -696,6 +700,8 @@ public class MainConfigurationPropertiesConfigurer extends
org.apache.camel.supp
case "RouteControllerThreadPoolSize": return
target.getRouteControllerThreadPoolSize();
case "routecontrollerunhealthyonexhausted":
case "RouteControllerUnhealthyOnExhausted": return
target.isRouteControllerUnhealthyOnExhausted();
+ case "routecontrollerunhealthyonrestarting":
+ case "RouteControllerUnhealthyOnRestarting": return
target.isRouteControllerUnhealthyOnRestarting();
case "routefilterexcludepattern":
case "RouteFilterExcludePattern": return
target.getRouteFilterExcludePattern();
case "routefilterincludepattern":
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 5577ce62cb1..1bf93cf0c12 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
@@ -93,6 +93,7 @@
{ "name": "camel.main.routeControllerSuperviseEnabled", "description": "To
enable using supervising route controller which allows Camel to startup and
then the controller takes care of starting the routes in a safe manner. This
can be used when you want to startup Camel despite a route may otherwise fail
fast during startup and cause Camel to fail to startup as well. By delegating
the route startup to the supervising route controller then its manages the
startup using a background th [...]
{ "name": "camel.main.routeControllerThreadPoolSize", "description": "The
number of threads used by the route controller scheduled thread pool that are
used for restarting routes. The pool uses 1 thread by default, but you can
increase this to allow the controller to concurrently attempt to restart
multiple routes in case more than one route has problems starting.",
"sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type":
"integer", "javaType": "int" },
{ "name": "camel.main.routeControllerUnhealthyOnExhausted", "description":
"Whether to mark the route as unhealthy (down) when all restarting attempts
(backoff) have failed and the route is not successfully started and the route
manager is giving up. Setting this to true allows health checks to know about
this and can report the Camel application as DOWN. The default is false.",
"sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type":
"boolean", "javaType": "bool [...]
+ { "name": "camel.main.routeControllerUnhealthyOnRestarting",
"description": "Whether to mark the route as unhealthy (down) when the route
failed to initially start, and is being controlled for restarting (backoff).
Setting this to true allows health checks to know about this and can report the
Camel application as DOWN. The default is false.", "sourceType":
"org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean",
"javaType": "boolean", "defaultValue": "false" },
{ "name": "camel.main.routeFilterExcludePattern", "description": "Used for
filtering routes routes matching the given pattern, which follows the following
rules: - Match by route id - Match by route input endpoint uri The matching is
using exact match, by wildcard and regular expression as documented by
PatternHelper#matchPattern(String,String) . For example to only include routes
which starts with foo in their route id's, use: include=foo* And to exclude
routes which starts from [...]
{ "name": "camel.main.routeFilterIncludePattern", "description": "Used for
filtering routes matching the given pattern, which follows the following rules:
- Match by route id - Match by route input endpoint uri The matching is using
exact match, by wildcard and regular expression as documented by
PatternHelper#matchPattern(String,String) . For example to only include routes
which starts with foo in their route id's, use: include=foo* And to exclude
routes which starts from JMS en [...]
{ "name": "camel.main.routesBuilderClasses", "description": "Sets classes
names that implement RoutesBuilder .", "sourceType":
"org.apache.camel.main.MainConfigurationProperties", "type": "string",
"javaType": "java.lang.String" },
diff --git a/core/camel-main/src/main/docs/main.adoc
b/core/camel-main/src/main/docs/main.adoc
index b4bbd92d60a..be566a90ec8 100644
--- a/core/camel-main/src/main/docs/main.adoc
+++ b/core/camel-main/src/main/docs/main.adoc
@@ -19,7 +19,7 @@ The following tables lists all the options:
// main options: START
=== Camel Main configurations
-The camel.main supports 127 options, which are listed below.
+The camel.main supports 128 options, which are listed below.
[width="100%",cols="2,5,^1,2",options="header"]
|===
@@ -100,6 +100,7 @@ The camel.main supports 127 options, which are listed below.
| *camel.main.routeController{zwsp}SuperviseEnabled* | To enable using
supervising route controller which allows Camel to startup and then the
controller takes care of starting the routes in a safe manner. This can be used
when you want to startup Camel despite a route may otherwise fail fast during
startup and cause Camel to fail to startup as well. By delegating the route
startup to the supervising route controller then its manages the startup using
a background thread. The controller [...]
| *camel.main.routeController{zwsp}ThreadPoolSize* | The number of threads
used by the route controller scheduled thread pool that are used for restarting
routes. The pool uses 1 thread by default, but you can increase this to allow
the controller to concurrently attempt to restart multiple routes in case more
than one route has problems starting. | | int
| *camel.main.routeController{zwsp}UnhealthyOnExhausted* | Whether to mark the
route as unhealthy (down) when all restarting attempts (backoff) have failed
and the route is not successfully started and the route manager is giving up.
Setting this to true allows health checks to know about this and can report the
Camel application as DOWN. The default is false. | false | boolean
+| *camel.main.routeController{zwsp}UnhealthyOnRestarting* | Whether to mark
the route as unhealthy (down) when the route failed to initially start, and is
being controlled for restarting (backoff). Setting this to true allows health
checks to know about this and can report the Camel application as DOWN. The
default is false. | false | boolean
| *camel.main.routeFilterExclude{zwsp}Pattern* | Used for filtering routes
routes matching the given pattern, which follows the following rules: - Match
by route id - Match by route input endpoint uri The matching is using exact
match, by wildcard and regular expression as documented by
PatternHelper#matchPattern(String,String) . For example to only include routes
which starts with foo in their route id's, use: include=foo* And to exclude
routes which starts from JMS endpoints, use: [...]
| *camel.main.routeFilterInclude{zwsp}Pattern* | Used for filtering routes
matching the given pattern, which follows the following rules: - Match by route
id - Match by route input endpoint uri The matching is using exact match, by
wildcard and regular expression as documented by
PatternHelper#matchPattern(String,String) . For example to only include routes
which starts with foo in their route id's, use: include=foo* And to exclude
routes which starts from JMS endpoints, use: exclude [...]
| *camel.main.routesBuilder{zwsp}Classes* | Sets classes names that implement
RoutesBuilder . | | String
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 a8c0a980f04..09bd2dceab3 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
@@ -378,6 +378,7 @@ public final class DefaultConfigurationConfigurer {
src.setBackOffMultiplier(config.getRouteControllerBackOffMultiplier());
}
src.setUnhealthyOnExhausted(config.isRouteControllerUnhealthyOnExhausted());
+
src.setUnhealthyOnRestarting(config.isRouteControllerUnhealthyOnRestarting());
}
}
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 d65aaecc9d1..d918b39705b 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
@@ -152,6 +152,7 @@ public abstract class DefaultConfigurationProperties<T> {
private long routeControllerBackOffMaxAttempts;
private double routeControllerBackOffMultiplier;
private boolean routeControllerUnhealthyOnExhausted;
+ private boolean routeControllerUnhealthyOnRestarting;
// startup recorder
@Metadata(enums = "false,off,java-flight-recorder,jfr,logging,backlog")
private String startupRecorder;
@@ -1654,6 +1655,22 @@ public abstract class DefaultConfigurationProperties<T> {
this.routeControllerUnhealthyOnExhausted =
routeControllerUnhealthyOnExhausted;
}
+ public boolean isRouteControllerUnhealthyOnRestarting() {
+ return routeControllerUnhealthyOnRestarting;
+ }
+
+ /**
+ * Whether to mark the route as unhealthy (down) when the route failed to
initially start, and is being controlled
+ * for restarting (backoff).
+ *
+ * Setting this to true allows health checks to know about this and can
report the Camel application as DOWN.
+ *
+ * The default is false.
+ */
+ public void setRouteControllerUnhealthyOnRestarting(boolean
routeControllerUnhealthyOnRestarting) {
+ this.routeControllerUnhealthyOnRestarting =
routeControllerUnhealthyOnRestarting;
+ }
+
public String getStartupRecorder() {
return startupRecorder;
}
@@ -2878,6 +2895,19 @@ public abstract class DefaultConfigurationProperties<T> {
return (T) this;
}
+ /**
+ * Whether to mark the route as unhealthy (down) when the route failed to
initially start, and is being controlled
+ * for restarting (backoff).
+ *
+ * Setting this to true allows health checks to know about this and can
report the Camel application as DOWN.
+ *
+ * The default is false.
+ */
+ public T withRouteControllerUnhealthyOnRestarting(boolean
routeControllerUnhealthyOnRestarting) {
+ this.routeControllerUnhealthyOnRestarting =
routeControllerUnhealthyOnRestarting;
+ return (T) this;
+ }
+
/**
* To use startup recorder for capturing execution time during starting
Camel. The recorder can be one of: false (or
* off), logging, backlog, java-flight-recorder (or jfr).
diff --git
a/core/camel-util/src/main/java/org/apache/camel/util/backoff/BackOff.java
b/core/camel-util/src/main/java/org/apache/camel/util/backoff/BackOff.java
index 7037a2f15d1..3939d50eed8 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/backoff/BackOff.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/backoff/BackOff.java
@@ -120,8 +120,12 @@ public final class BackOff {
if (maxElapsedTime != MAX_DURATION) {
sb.append(", maxElapsedTime=").append(maxElapsedTime.toMillis());
}
- sb.append(", maxAttempts=").append(maxAttempts);
- sb.append(", multiplier=").append(multiplier);
+ if (maxAttempts != Long.MAX_VALUE) {
+ sb.append(", maxAttempts=").append(maxAttempts);
+ }
+ if (multiplier != DEFAULT_MULTIPLIER) {
+ sb.append(", multiplier=").append(multiplier);
+ }
sb.append("]");
return sb.toString();
}
diff --git a/docs/user-manual/modules/ROOT/pages/route-controller.adoc
b/docs/user-manual/modules/ROOT/pages/route-controller.adoc
index 095a042b1ee..0d3dd197f1c 100644
--- a/docs/user-manual/modules/ROOT/pages/route-controller.adoc
+++ b/docs/user-manual/modules/ROOT/pages/route-controller.adoc
@@ -143,6 +143,8 @@ You can configure the `SupervisingRouteController` using
the following options:
| IncludeRoutes | | Pattern for filtering routes to be included as supervised.
The pattern is matching on route id, and endpoint uri for the route. Multiple
patterns can be separated by comma. For example to include all kafka routes,
you can say kafka:. And to include routes with specific route ids
myRoute,myOtherRoute. The pattern supports wildcards and uses the matcher from
org.apache.camel.support.PatternHelper#matchPattern.
| ExcludeRoutes | | Pattern for filtering routes to be excluded as supervised.
The pattern is matching on route id, and endpoint uri for the route. Multiple
patterns can be separated by comma. For example to exclude all JMS routes, you
can say jms:. And to exclude routes with specific route ids
mySpecialRoute,myOtherSpecialRoute. The pattern supports wildcards and uses the
matcher from org.apache.camel.support.PatternHelper#matchPattern.
| ThreadPoolSize | `1` | The number of threads used by the route controller
scheduled thread pool that are used for restarting routes. The pool uses 1
thread by default, but you can increase this to allow the controller to
concurrently attempt to restart multiple routes in case more than one route has
problems starting.
+| UnhealthyOnExhausted | `false` | Whether to mark the route as unhealthy
(down) when all restarting attempts (backoff) have failed and the route is not
successfully started and the route manager is giving up. Setting this to true
allows health checks to know about this and can report the Camel application as
DOWN.
+| routeControllerUnhealthyOnRestarting | `false` | Whether to mark the route
as unhealthy (down) when the route failed to initially start, and is being
controlled for restarting (backoff). Setting this to true allows health checks
to know about this and can report the Camel application as DOWN.
|=======================================================================
=== Filtering routes to fail fast
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/RouteControllerAction.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/RouteControllerAction.java
index 08554025b69..8a85c25459a 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/RouteControllerAction.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/RouteControllerAction.java
@@ -133,7 +133,7 @@ public class RouteControllerAction extends
ActionWatchCommand {
row.attempts = jt.getLong("attempts");
long time = jt.getLong("lastAttempt");
if (time > 0) {
- row.lastAttempt = TimeUtils.printDuration(time);
+ row.lastAttempt = TimeUtils.printSince(time);
}
time = jt.getLong("nextAttempt");
if (time > 0) {
@@ -165,6 +165,8 @@ public class RouteControllerAction extends
ActionWatchCommand {
if (supervising) {
if (header) {
System.out.println("Supervising Route Controller");
+ System.out.printf("\tInitial Starting Routes: %b%n",
jo.getBoolean("startingRoutes"));
+ System.out.printf("\tUnhealthy Routes: %b%n",
jo.getBoolean("unhealthyRoutes"));
System.out.printf("\tRoutes Total: %s%n",
jo.getInteger("totalRoutes"));
System.out.printf("\tRoutes Started: %d%n",
jo.getInteger("startedRoutes"));
System.out.printf("\tRoutes Restarting: %d%n",
jo.getInteger("restartingRoutes"));
@@ -175,13 +177,15 @@ public class RouteControllerAction extends
ActionWatchCommand {
System.out.printf("\tBackoff Max Elapsed Time: %d%n",
jo.getInteger("backoffMaxElapsedTime"));
System.out.printf("\tBackoff Max Attempts: %d%n",
jo.getInteger("backoffMaxAttempts"));
System.out.printf("\tThread Pool Size: %d%n",
jo.getInteger("threadPoolSize"));
- System.out.printf("\tUnhealthy on Exhaust: %b%n",
jo.getBoolean("unhealthyOnExhausted"));
+ System.out.printf("\tUnhealthy On Restarting: %b%n",
jo.getBoolean("unhealthyOnRestarting"));
+ System.out.printf("\tUnhealthy On Exhaust: %b%n",
jo.getBoolean("unhealthyOnExhausted"));
System.out.println("\n");
}
dumpTable(rows, true);
} else {
if (header) {
System.out.println("Default Route Controller");
+ System.out.printf("\tStarting Routes: %b%n",
jo.getBoolean("startingRoutes"));
System.out.printf("\tRoutes Total: %s%n",
jo.getInteger("totalRoutes"));
System.out.println("\n");
}
@@ -204,6 +208,7 @@ public class RouteControllerAction extends
ActionWatchCommand {
new
Column().header("STATE").headerAlign(HorizontalAlign.RIGHT).with(this::getSupervising),
new
Column().visible(supervised).header("ATTEMPT").headerAlign(HorizontalAlign.CENTER)
.dataAlign(HorizontalAlign.CENTER).with(this::getAttempts),
+ new
Column().visible(supervised).header("ELAPSED").headerAlign(HorizontalAlign.CENTER).with(this::getElapsed),
new
Column().visible(supervised).header("LAST-AGO").headerAlign(HorizontalAlign.CENTER).with(this::getLast),
new
Column().visible(supervised).header("ERROR-MESSAGE").headerAlign(HorizontalAlign.LEFT)
.dataAlign(HorizontalAlign.LEFT)
@@ -291,11 +296,14 @@ public class RouteControllerAction extends
ActionWatchCommand {
protected String getLast(Row r) {
if (r.lastAttempt != null && !r.lastAttempt.isEmpty()) {
- String s = r.lastAttempt;
- if (r.elapsed != null && !r.elapsed.isEmpty()) {
- s += " (" + r.elapsed + ")";
- }
- return s;
+ return r.lastAttempt;
+ }
+ return "";
+ }
+
+ protected String getElapsed(Row r) {
+ if (r.elapsed != null && !r.elapsed.isEmpty()) {
+ return r.elapsed;
}
return "";
}