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/10/23 09:27:40 UTC
[camel] 01/05: CAMEL-18630: camel-jbang - Add get health command
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 ff79be2fe85469e74acd7e0641b375b01c835f4a
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sun Oct 23 10:36:00 2022 +0200
CAMEL-18630: camel-jbang - Add get health command
---
.../camel/impl/health/AbstractHealthCheck.java | 4 +
.../dsl/jbang/core/commands/CamelJBangMain.java | 2 +
.../jbang/core/commands/process/ListHealth.java | 229 +++++++++++++++++++++
3 files changed, 235 insertions(+)
diff --git a/core/camel-health/src/main/java/org/apache/camel/impl/health/AbstractHealthCheck.java b/core/camel-health/src/main/java/org/apache/camel/impl/health/AbstractHealthCheck.java
index 3b1767ade2a..d5d218d18c8 100644
--- a/core/camel-health/src/main/java/org/apache/camel/impl/health/AbstractHealthCheck.java
+++ b/core/camel-health/src/main/java/org/apache/camel/impl/health/AbstractHealthCheck.java
@@ -145,6 +145,10 @@ public abstract class AbstractHealthCheck implements HealthCheck, CamelContextAw
kind = isLiveness() ? Kind.LIVENESS : Kind.READINESS;
}
builder.detail(CHECK_KIND, kind);
+ builder.detail(CHECK_ID, meta.get(CHECK_ID));
+ if (meta.containsKey(CHECK_GROUP)) {
+ builder.detail(CHECK_GROUP, meta.get(CHECK_GROUP));
+ }
// Extract relevant information from meta data.
int invocationCount = (Integer) meta.getOrDefault(INVOCATION_COUNT, 0);
int failureCount = (Integer) meta.getOrDefault(FAILURE_COUNT, 0);
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
index 65999c69f5a..44edb4a1862 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
@@ -51,6 +51,7 @@ import org.apache.camel.dsl.jbang.core.commands.process.Hawtio;
import org.apache.camel.dsl.jbang.core.commands.process.Jolokia;
import org.apache.camel.dsl.jbang.core.commands.process.ListBlocked;
import org.apache.camel.dsl.jbang.core.commands.process.ListEvent;
+import org.apache.camel.dsl.jbang.core.commands.process.ListHealth;
import org.apache.camel.dsl.jbang.core.commands.process.ListInflight;
import org.apache.camel.dsl.jbang.core.commands.process.ListProcess;
import org.apache.camel.dsl.jbang.core.commands.process.ListService;
@@ -74,6 +75,7 @@ public class CamelJBangMain implements Callable<Integer> {
.addSubcommand("context", new CommandLine(new CamelContextStatus(main)))
.addSubcommand("route", new CommandLine(new CamelRouteStatus(main)))
.addSubcommand("processor", new CommandLine(new CamelProcessorStatus(main)))
+ .addSubcommand("health", new CommandLine(new ListHealth(main)))
.addSubcommand("endpoint", new CommandLine(new CamelEndpointStatus(main)))
.addSubcommand("event", new CommandLine(new ListEvent(main)))
.addSubcommand("inflight", new CommandLine(new ListInflight(main)))
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListHealth.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListHealth.java
new file mode 100644
index 00000000000..0e991e024dd
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListHealth.java
@@ -0,0 +1,229 @@
+/*
+ * 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.dsl.jbang.core.commands.process;
+
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.github.freva.asciitable.AsciiTable;
+import com.github.freva.asciitable.Column;
+import com.github.freva.asciitable.HorizontalAlign;
+import com.github.freva.asciitable.OverflowBehaviour;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.util.TimeUtils;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+
+@Command(name = "health", description = "Get health check status of running Camel integrations")
+public class ListHealth extends ProcessBaseCommand {
+
+ @CommandLine.Option(names = { "--sort" },
+ description = "Sort by pid, name or age", defaultValue = "pid")
+ String sort;
+
+ @CommandLine.Option(names = { "--level" },
+ description = "Level of details: full, oneline, or default", defaultValue = "default")
+ String level;
+
+ @CommandLine.Option(names = { "--down" },
+ description = "Show only checks which are DOWN")
+ boolean down;
+
+ @CommandLine.Option(names = { "--ready" },
+ description = "Show only readiness checks")
+ boolean ready;
+
+ @CommandLine.Option(names = { "--live" },
+ description = "Show only liveness checks")
+ boolean live;
+
+ public ListHealth(CamelJBangMain main) {
+ super(main);
+ }
+
+ @Override
+ public Integer call() throws Exception {
+ List<Row> rows = new ArrayList<>();
+
+ List<Long> pids = findPids("*");
+ ProcessHandle.allProcesses()
+ .filter(ph -> pids.contains(ph.pid()))
+ .forEach(ph -> {
+ JsonObject root = loadStatus(ph.pid());
+ if (root != null) {
+ JsonObject context = (JsonObject) root.get("context");
+ if (context == null) {
+ return;
+ }
+ JsonObject hc = (JsonObject) root.get("healthChecks");
+ if (hc == null) {
+ return;
+ }
+ JsonArray array = (JsonArray) hc.get("checks");
+ for (int i = 0; i < array.size(); i++) {
+ JsonObject o = (JsonObject) array.get(i);
+ Row row = new Row();
+ row.pid = "" + ph.pid();
+ row.uptime = extractSince(ph);
+ row.ago = TimeUtils.printSince(row.uptime);
+ row.name = context.getString("name");
+ if ("CamelJBang".equals(row.name)) {
+ row.name = extractName(root, ph);
+ }
+ row.id = o.getString("id");
+ row.group = o.getString("group");
+ row.state = o.getString("state");
+ row.readiness = o.getBoolean("readiness");
+ row.liveness = o.getBoolean("liveness");
+ row.message = o.getString("message");
+
+ JsonObject d = (JsonObject) o.get("details");
+ if (d != null) {
+ row.total = d.getString("invocation.count");
+ row.success = d.getString("success.count");
+ row.failure = d.getString("failure.count");
+ String kind = d.getString("check.kind");
+ if ("READINESS".equals(kind)) {
+ row.liveness = false;
+ } else if ("LIVENESS".equals(kind)) {
+ row.readiness = false;
+ }
+ // calc how long time since the check was invoked
+ String time = d.getString("invocation.time");
+ if (time != null) {
+ ZonedDateTime zdt = ZonedDateTime.parse(time);
+ if (zdt != null) {
+ long delta = Math.abs(ZonedDateTime.now().until(zdt, ChronoUnit.MILLIS));
+ row.since = TimeUtils.printAge(delta);
+ }
+ }
+ }
+
+ boolean add = true;
+ if (live && !row.liveness) {
+ add = false;
+ }
+ if (ready && !row.readiness) {
+ add = false;
+ }
+ if (down && !row.state.equals("DOWN")) {
+ add = false;
+ }
+ if (add) {
+ rows.add(row);
+ }
+ }
+ }
+ });
+
+ // sort rows
+ rows.sort(this::sortRow);
+
+ if (!rows.isEmpty()) {
+ System.out.println(AsciiTable.getTable(AsciiTable.NO_BORDERS, rows, Arrays.asList(
+ new Column().header("PID").headerAlign(HorizontalAlign.CENTER).with(r -> r.pid),
+ new Column().header("NAME").dataAlign(HorizontalAlign.LEFT)
+ .maxWidth(40, OverflowBehaviour.ELLIPSIS_RIGHT)
+ .with(r -> r.name),
+ new Column().header("AGE").headerAlign(HorizontalAlign.CENTER).with(r -> r.ago),
+ new Column().header("ID").dataAlign(HorizontalAlign.LEFT)
+ .maxWidth(40, OverflowBehaviour.ELLIPSIS_RIGHT)
+ .with(this::getId),
+ new Column().header("SINCE").headerAlign(HorizontalAlign.RIGHT)
+ .dataAlign(HorizontalAlign.RIGHT)
+ .with(r -> r.since),
+ new Column().header("STATE").headerAlign(HorizontalAlign.CENTER)
+ .dataAlign(HorizontalAlign.CENTER)
+ .with(r -> r.state),
+ new Column().header("RL").minWidth(4).maxWidth(4).with(this::getLR),
+ new Column().header("TOTAL").headerAlign(HorizontalAlign.RIGHT).with(r -> r.total),
+ new Column().header("OK/KO").headerAlign(HorizontalAlign.RIGHT).with(this::getRate),
+ new Column().header("MESSAGE").dataAlign(HorizontalAlign.LEFT)
+ .maxWidth(80, OverflowBehaviour.NEWLINE)
+ .with(r -> r.message))));
+ }
+
+ return 0;
+ }
+
+ protected int sortRow(Row o1, Row o2) {
+ String s = sort;
+ int negate = 1;
+ if (s.startsWith("-")) {
+ s = s.substring(1);
+ negate = -1;
+ }
+ switch (s) {
+ case "pid":
+ return Long.compare(Long.parseLong(o1.pid), Long.parseLong(o2.pid)) * negate;
+ case "name":
+ return o1.name.compareToIgnoreCase(o2.name) * negate;
+ case "age":
+ return Long.compare(o1.uptime, o2.uptime) * negate;
+ default:
+ return 0;
+ }
+ }
+
+ protected String getId(Row r) {
+ if (r.group != null) {
+ return r.group + "/" + r.id;
+ } else {
+ return r.id;
+ }
+ }
+
+ protected String getLR(Row r) {
+ if (r.readiness && r.liveness) {
+ return "RL";
+ } else if (r.readiness) {
+ return "R";
+ } else if (r.liveness) {
+ return "L";
+ }
+ return "";
+ }
+
+ protected String getRate(Row r) {
+ String s1 = r.success != null && !"0".equals(r.success) ? r.success : "-";
+ String s2 = r.failure != null && !"0".equals(r.failure) ? r.failure : "-";
+ return s1 + "/" + s2;
+ }
+
+ private static class Row {
+ String pid;
+ String name;
+ String ago;
+ long uptime;
+ String id;
+ String group;
+ String state;
+ boolean readiness;
+ boolean liveness;
+ String total;
+ String success;
+ String failure;
+ String since;
+ String message;
+ }
+
+}