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 471a17fc9890a4d19fabc6592184a1c54b689ae6 Author: Claus Ibsen <[email protected]> AuthorDate: Mon Aug 29 14:20:51 2022 +0200 CAMEL-18425: camel-cli - Display more information for top commands. --- .../GarbageCollectorDevConsoleConfigurer.java | 49 +++++ .../impl/console/ThreadDevConsoleConfigurer.java | 49 +++++ ...e.camel.impl.console.GarbageCollectorDevConsole | 2 + .../org.apache.camel.impl.console.ThreadDevConsole | 2 + .../services/org/apache/camel/dev-console/gc | 2 + .../services/org/apache/camel/dev-console/thread | 2 + .../impl/console/GarbageCollectorDevConsole.java | 73 +++++++ .../camel/impl/console/ThreadDevConsole.java | 101 ++++++++++ .../camel/cli/connector/LocalCliConnector.java | 84 +++++++- .../dsl/jbang/core/commands/CamelJBangMain.java | 10 +- .../core/commands/process/CamelContextTop.java | 222 +++++++++++++++++++++ .../{CamelTopStatus.java => CamelRouteTop.java} | 6 +- .../process/{CamelTopStatus.java => CamelTop.java} | 26 +-- 13 files changed, 605 insertions(+), 23 deletions(-) diff --git a/core/camel-console/src/generated/java/org/apache/camel/impl/console/GarbageCollectorDevConsoleConfigurer.java b/core/camel-console/src/generated/java/org/apache/camel/impl/console/GarbageCollectorDevConsoleConfigurer.java new file mode 100644 index 00000000000..6e11e40485e --- /dev/null +++ b/core/camel-console/src/generated/java/org/apache/camel/impl/console/GarbageCollectorDevConsoleConfigurer.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.GarbageCollectorDevConsole; + +/** + * Generated by camel build tools - do NOT edit this file! + */ +@SuppressWarnings("unchecked") +public class GarbageCollectorDevConsoleConfigurer 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.GarbageCollectorDevConsole target = (org.apache.camel.impl.console.GarbageCollectorDevConsole) 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.GarbageCollectorDevConsole target = (org.apache.camel.impl.console.GarbageCollectorDevConsole) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "camelcontext": + case "CamelContext": return target.getCamelContext(); + default: return null; + } + } +} + diff --git a/core/camel-console/src/generated/java/org/apache/camel/impl/console/ThreadDevConsoleConfigurer.java b/core/camel-console/src/generated/java/org/apache/camel/impl/console/ThreadDevConsoleConfigurer.java new file mode 100644 index 00000000000..6df6dd7ab60 --- /dev/null +++ b/core/camel-console/src/generated/java/org/apache/camel/impl/console/ThreadDevConsoleConfigurer.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.ThreadDevConsole; + +/** + * Generated by camel build tools - do NOT edit this file! + */ +@SuppressWarnings("unchecked") +public class ThreadDevConsoleConfigurer 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.ThreadDevConsole target = (org.apache.camel.impl.console.ThreadDevConsole) 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.ThreadDevConsole target = (org.apache.camel.impl.console.ThreadDevConsole) 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.GarbageCollectorDevConsole b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.GarbageCollectorDevConsole new file mode 100644 index 00000000000..da5eb35d631 --- /dev/null +++ b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.GarbageCollectorDevConsole @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.impl.console.GarbageCollectorDevConsoleConfigurer diff --git a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.ThreadDevConsole b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.ThreadDevConsole new file mode 100644 index 00000000000..6aff17f333c --- /dev/null +++ b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.ThreadDevConsole @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.impl.console.ThreadDevConsoleConfigurer diff --git a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/gc b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/gc new file mode 100644 index 00000000000..b3f7a71ee18 --- /dev/null +++ b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/gc @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.impl.console.GarbageCollectorDevConsole diff --git a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/thread b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/thread new file mode 100644 index 00000000000..e34acd483f6 --- /dev/null +++ b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/thread @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.impl.console.ThreadDevConsole diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/GarbageCollectorDevConsole.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/GarbageCollectorDevConsole.java new file mode 100644 index 00000000000..9e2bdc6ee3a --- /dev/null +++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/GarbageCollectorDevConsole.java @@ -0,0 +1,73 @@ +/* + * 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.spi.Configurer; +import org.apache.camel.spi.annotations.DevConsole; +import org.apache.camel.util.json.JsonArray; +import org.apache.camel.util.json.JsonObject; + +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static org.apache.camel.util.UnitUtils.printUnitFromBytesDot; + +@DevConsole("gc") +@Configurer(bootstrap = true) +public class GarbageCollectorDevConsole extends AbstractDevConsole { + + public GarbageCollectorDevConsole() { + super("jvm", "gc", "Garbage Collector", "Displays Garbage Collector information"); + } + + @Override + protected String doCallText(Map<String, Object> options) { + StringBuilder sb = new StringBuilder(); + + List<GarbageCollectorMXBean> gcs = ManagementFactory.getGarbageCollectorMXBeans(); + if (gcs != null && !gcs.isEmpty()) { + for (GarbageCollectorMXBean gc : gcs) { + sb.append(String.format("\n %s: %s (%s ms)", gc.getName(), gc.getCollectionCount(), gc.getCollectionTime())); + } + } + + return sb.toString(); + } + + @Override + protected JsonObject doCallJson(Map<String, Object> options) { + JsonObject root = new JsonObject(); + JsonArray arr = new JsonArray(); + root.put("garbageCollectors", arr); + + List<GarbageCollectorMXBean> gcs = ManagementFactory.getGarbageCollectorMXBeans(); + if (gcs != null && !gcs.isEmpty()) { + for (GarbageCollectorMXBean gc : gcs) { + JsonObject jo = new JsonObject(); + arr.add(jo); + jo.put("name", gc.getName()); + jo.put("collectionCount", gc.getCollectionCount()); + jo.put("collectionTime", gc.getCollectionTime()); + jo.put("memoryPoolNames", String.join(",", Arrays.asList(gc.getMemoryPoolNames()))); + } + } + return root; + } +} diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/ThreadDevConsole.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/ThreadDevConsole.java new file mode 100644 index 00000000000..ed98ea22724 --- /dev/null +++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/ThreadDevConsole.java @@ -0,0 +1,101 @@ +/* + * 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.spi.Configurer; +import org.apache.camel.spi.annotations.DevConsole; +import org.apache.camel.util.json.JsonArray; +import org.apache.camel.util.json.JsonObject; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.Arrays; +import java.util.Map; + +@DevConsole("thread") +@Configurer(bootstrap = true) +public class ThreadDevConsole extends AbstractDevConsole { + + public ThreadDevConsole() { + super("jvm", "thread", "Thread", "Displays Threads information"); + } + + @Override + protected String doCallText(Map<String, Object> options) { + StringBuilder sb = new StringBuilder(); + + ThreadMXBean tb = ManagementFactory.getThreadMXBean(); + if (tb != null) { + sb.append(String.format("Threads: %s\n", tb.getThreadCount())); + sb.append(String.format("Daemon Threads: %s\n", tb.getDaemonThreadCount())); + sb.append(String.format("Total Started Threads: %s\n", tb.getTotalStartedThreadCount())); + sb.append(String.format("Peak Threads: %s\n", tb.getPeakThreadCount())); + + long[] ids = tb.getAllThreadIds(); + Arrays.sort(ids); + for (long id : ids) { + ThreadInfo ti = tb.getThreadInfo(id); + if (ti != null) { + String lock = ti.getLockName() != null ? "locked: " + ti.getLockName() : ""; + sb.append(String.format("\n Thread %s: %s (%s) %s", id, ti.getThreadName(), ti.getThreadState(), lock)); + } + } + } + + return sb.toString(); + } + + @Override + protected JsonObject doCallJson(Map<String, Object> options) { + JsonObject root = new JsonObject(); + + ThreadMXBean tb = ManagementFactory.getThreadMXBean(); + if (tb != null) { + root.put("threadCount", tb.getThreadCount()); + root.put("daemonThreadCount", tb.getDaemonThreadCount()); + root.put("totalStartedThreadCount", tb.getTotalStartedThreadCount()); + root.put("peakThreadCount", tb.getPeakThreadCount()); + + JsonArray arr = new JsonArray(); + root.put("threads", arr); + + long[] ids = tb.getAllThreadIds(); + Arrays.sort(ids); + for (long id : ids) { + ThreadInfo ti = tb.getThreadInfo(id); + if (ti != null) { + JsonObject jo = new JsonObject(); + jo.put("id", ti.getThreadId()); + jo.put("name", ti.getThreadName()); + jo.put("state", ti.getThreadState()); + jo.put("blockedCount", ti.getBlockedCount()); + jo.put("blockedTime", ti.getBlockedTime()); + jo.put("waitedCount", ti.getWaitedCount()); + jo.put("waitedTime", ti.getWaitedTime()); + if (ti.getLockName() != null) { + jo.put("lockName", ti.getLockName()); + } + arr.add(jo); + } + } + } + + return root; + } + +} diff --git a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java index d51a7f79a27..f26d00b5afd 100644 --- a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java +++ b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java @@ -17,7 +17,14 @@ package org.apache.camel.cli.connector; import java.io.File; +import java.lang.management.ClassLoadingMXBean; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.RuntimeMXBean; +import java.lang.management.ThreadMXBean; import java.util.Collection; +import java.util.List; import java.util.Locale; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -168,11 +175,15 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C ProcessHandle.current().info().user().ifPresent(u -> rc.put("user", u)); rc.put("platform", platform); if (platformVersion != null) { - rc.put("version", platformVersion); + rc.put("platformVersion", platformVersion); } if (mainClass != null) { rc.put("mainClass", mainClass); } + RuntimeMXBean mb = ManagementFactory.getRuntimeMXBean(); + if (mb != null) { + rc.put("javaVersion", mb.getVmVersion()); + } root.put("runtime", rc); // collect details via console @@ -190,6 +201,23 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C root.put("routes", json2.get("routes")); } } + // various details + JsonObject mem = collectMemory(); + if (mem != null) { + root.put("memory", mem); + } + JsonObject cl = collectClassLoading(); + if (cl != null) { + root.put("classLoading", cl); + } + JsonObject threads = collectThreads(); + if (threads != null) { + root.put("threads", threads); + } + JsonObject gc = collectGC(); + if (gc != null) { + root.put("gc", gc); + } // and health-check readiness Collection<HealthCheck.Result> res = HealthCheckHelper.invokeReadiness(camelContext); for (var r : res) { @@ -213,6 +241,60 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C } } + private JsonObject collectMemory() { + MemoryMXBean mb = ManagementFactory.getMemoryMXBean(); + if (mb != null) { + JsonObject root = new JsonObject(); + root.put("heapMemoryUsed", mb.getHeapMemoryUsage().getUsed()); + root.put("heapMemoryCommitted", mb.getHeapMemoryUsage().getCommitted()); + root.put("heapMemoryMax", mb.getHeapMemoryUsage().getMax()); + root.put("nonHeapMemoryUsed", mb.getNonHeapMemoryUsage().getUsed()); + root.put("nonHeapMemoryCommitted", mb.getNonHeapMemoryUsage().getCommitted()); + return root; + } + return null; + } + + private JsonObject collectClassLoading() { + ClassLoadingMXBean cb = ManagementFactory.getClassLoadingMXBean(); + if (cb != null) { + JsonObject root = new JsonObject(); + root.put("loadedClassCount", cb.getLoadedClassCount()); + root.put("unloadedClassCount", cb.getUnloadedClassCount()); + root.put("totalLoadedClassCount", cb.getTotalLoadedClassCount()); + return root; + } + return null; + } + + private JsonObject collectThreads() { + ThreadMXBean tb = ManagementFactory.getThreadMXBean(); + if (tb != null) { + JsonObject root = new JsonObject(); + root.put("threadCount", tb.getThreadCount()); + root.put("peakThreadCount", tb.getPeakThreadCount()); + return root; + } + return null; + } + + private JsonObject collectGC() { + List<GarbageCollectorMXBean> gcs = ManagementFactory.getGarbageCollectorMXBeans(); + if (gcs != null && !gcs.isEmpty()) { + JsonObject root = new JsonObject(); + long count = 0; + long time = 0; + for (GarbageCollectorMXBean gc : gcs) { + count += gc.getCollectionCount(); + time += gc.getCollectionTime(); + } + root.put("collectionCount", count); + root.put("collectionTime", time); + return root; + } + return null; + } + @Override protected void doStop() throws Exception { // cleanup 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 e19a4f0f55f..32448d38b62 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 @@ -21,9 +21,11 @@ import java.util.concurrent.Callable; import org.apache.camel.catalog.CamelCatalog; import org.apache.camel.catalog.DefaultCamelCatalog; import org.apache.camel.dsl.jbang.core.commands.process.CamelContextStatus; +import org.apache.camel.dsl.jbang.core.commands.process.CamelContextTop; import org.apache.camel.dsl.jbang.core.commands.process.CamelRouteStatus; +import org.apache.camel.dsl.jbang.core.commands.process.CamelRouteTop; import org.apache.camel.dsl.jbang.core.commands.process.CamelStatus; -import org.apache.camel.dsl.jbang.core.commands.process.CamelTopStatus; +import org.apache.camel.dsl.jbang.core.commands.process.CamelTop; 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.ListProcess; @@ -44,8 +46,10 @@ public class CamelJBangMain implements Callable<Integer> { .addSubcommand("stop", new CommandLine(new StopProcess(main))) .addSubcommand("get", new CommandLine(new CamelStatus(main)) .addSubcommand("context", new CommandLine(new CamelContextStatus(main))) - .addSubcommand("route", new CommandLine(new CamelRouteStatus(main))) - .addSubcommand("top", new CommandLine(new CamelTopStatus(main)))) + .addSubcommand("route", new CommandLine(new CamelRouteStatus(main)))) + .addSubcommand("top", new CommandLine(new CamelTop(main)) + .addSubcommand("context", new CommandLine(new CamelContextTop(main))) + .addSubcommand("route", new CommandLine(new CamelRouteTop(main)))) .addSubcommand("generate", new CommandLine(new CodeGenerator(main)) .addSubcommand("rest", new CommandLine(new CodeRestGenerator(main)))) .addSubcommand("jolokia", new CommandLine(new Jolokia(main))) diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelContextTop.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelContextTop.java new file mode 100644 index 00000000000..2c51d4acfa5 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelContextTop.java @@ -0,0 +1,222 @@ +/* + * 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.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.JsonObject; +import picocli.CommandLine; +import picocli.CommandLine.Command; + +@Command(name = "integration", aliases = { "int", "integration", "integrations", "context" }, + description = "Top status of Camel integrations") +public class CamelContextTop extends ProcessBaseCommand { + + @CommandLine.Parameters(description = "Name or pid of running Camel integration", arity = "0..1") + String name = "*"; + + @CommandLine.Option(names = { "--sort" }, + description = "Sort by pid, name, mem, or age", defaultValue = "mem") + String sort; + + public CamelContextTop(CamelJBangMain main) { + super(main); + } + + @Override + public Integer call() throws Exception { + List<Row> rows = new ArrayList<>(); + + List<Long> pids = findPids(name); + ProcessHandle.allProcesses() + .filter(ph -> pids.contains(ph.pid())) + .forEach(ph -> { + JsonObject root = loadStatus(ph.pid()); + // there must be a status file for the running Camel integration + if (root != null) { + Row row = new Row(); + rows.add(row); + row.name = extractName(root, ph); + row.pid = "" + ph.pid(); + row.uptime = extractSince(ph); + row.ago = TimeUtils.printSince(row.uptime); + JsonObject runtime = (JsonObject) root.get("runtime"); + row.platform = extractPlatform(ph, runtime); + row.platformVersion = runtime != null ? runtime.getString("platformVersion") : null; + row.javaVersion = runtime != null ? runtime.getString("javaVersion") : null; + JsonObject context = (JsonObject) root.get("context"); + row.state = context.getInteger("phase"); + row.camelVersion = context.getString("version"); + + JsonObject mem = (JsonObject) root.get("memory"); + if (mem != null) { + row.heapMemUsed = mem.getLong("heapMemoryUsed"); + row.heapMemCommitted = mem.getLong("heapMemoryCommitted"); + row.heapMemMax = mem.getLong("heapMemoryMax"); + row.nonHeapMemUsed = mem.getLong("nonHeapMemoryUsed"); + row.nonHeapMemCommitted = mem.getLong("nonHeapMemoryCommitted"); + } + JsonObject threads = (JsonObject) root.get("threads"); + if (threads != null) { + row.threadCount = threads.getInteger("threadCount"); + row.peakThreadCount = threads.getInteger("peakThreadCount"); + } + JsonObject cl = (JsonObject) root.get("classLoading"); + if (cl != null) { + row.loadedClassCount = cl.getInteger("loadedClassCount"); + row.totalLoadedClassCount = cl.getLong("totalLoadedClassCount"); + } + JsonObject gc = (JsonObject) root.get("gc"); + if (gc != null) { + row.gcCount = gc.getLong("collectionCount"); + row.gcTime = gc.getLong("collectionTime"); + } + } + }); + + // 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(30, OverflowBehaviour.ELLIPSIS) + .with(r -> r.name), + new Column().header("JAVA").dataAlign(HorizontalAlign.LEFT).with(this::getJavaVersion), + new Column().header("CAMEL").dataAlign(HorizontalAlign.LEFT).with(r -> r.camelVersion), + new Column().header("PLATFORM").dataAlign(HorizontalAlign.LEFT).with(this::getPlatform), + new Column().header("STATUS").headerAlign(HorizontalAlign.CENTER) + .with(r -> extractState(r.state)), + new Column().header("AGE").headerAlign(HorizontalAlign.CENTER).with(r -> r.ago), + new Column().header("HEAP").headerAlign(HorizontalAlign.CENTER).with(this::getHeapMemory), + new Column().header("NON-HEAP").headerAlign(HorizontalAlign.CENTER).with(this::getNonHeapMemory), + new Column().header("GC").headerAlign(HorizontalAlign.CENTER).with(this::getGC), + new Column().header("THREADS").headerAlign(HorizontalAlign.CENTER).with(this::getThreads), + new Column().header("CLASSES").headerAlign(HorizontalAlign.CENTER).with(this::getClassLoading)))); + } + + return 0; + } + + private String extractPlatform(ProcessHandle ph, JsonObject runtime) { + String answer = runtime != null ? runtime.getString("platform") : null; + if ("Camel".equals(answer)) { + // generic camel, we need to check if we run in JBang + String cl = ph.info().commandLine().orElse(""); + if (cl.contains("main.CamelJBang run")) { + answer = "JBang"; + } + } + return answer; + } + + protected int sortRow(Row o1, Row o2) { + switch (sort) { + case "pid": + return Long.compare(Long.parseLong(o1.pid), Long.parseLong(o2.pid)); + case "name": + return o1.name.compareToIgnoreCase(o2.name); + case "mem": + return Long.compare(o1.heapMemUsed, o2.heapMemUsed) * -1; // we want the biggest first + case "age": + return Long.compare(o1.uptime, o2.uptime); + default: + return 0; + } + } + + private String getPlatform(Row r) { + if (r.platformVersion != null) { + return r.platform + " v" + r.platformVersion; + } else { + return r.platform; + } + } + + private String getHeapMemory(Row r) { + return asMegaBytesOneDigit(r.heapMemUsed) + "/" + asMegaBytesOneDigit(r.heapMemCommitted) + "/" + + asMegaBytesOneDigit(r.heapMemMax) + " MB"; + } + + private String getNonHeapMemory(Row r) { + return asMegaBytesOneDigit(r.nonHeapMemUsed) + "/" + asMegaBytesOneDigit(r.nonHeapMemCommitted) + " MB"; + } + + private String getThreads(Row r) { + return r.threadCount + "/" + r.peakThreadCount; + } + + private String getClassLoading(Row r) { + return r.loadedClassCount + "/" + r.totalLoadedClassCount; + } + + private String getGC(Row r) { + if (r.gcTime <= 0) { + return ""; + } else { + return r.gcTime + "ms (" + r.gcCount + ")"; + } + } + + private String getJavaVersion(Row r) { + String v = r.javaVersion; + for (int i = 0; i < v.length(); i++) { + char ch = v.charAt(i); + if (Character.isDigit(ch) || ch == '.') { + continue; + } + return v.substring(0, i); + } + return v; + } + + private static long asMegaBytesOneDigit(long bytes) { + return bytes / 1000 / 1000; + } + + private static class Row { + String pid; + String platform; + String platformVersion; + String camelVersion; + String javaVersion; + String name; + int state; + String ago; + long uptime; + long heapMemUsed; + long heapMemCommitted; + long heapMemMax; + long nonHeapMemUsed; + long nonHeapMemCommitted; + int threadCount; + int peakThreadCount; + int loadedClassCount; + long totalLoadedClassCount; + long gcCount; + long gcTime; + } + +} diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelTopStatus.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelRouteTop.java similarity index 89% copy from dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelTopStatus.java copy to dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelRouteTop.java index 44d6e8a17ea..afef2e6bb1c 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelTopStatus.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelRouteTop.java @@ -19,10 +19,10 @@ package org.apache.camel.dsl.jbang.core.commands.process; import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; import picocli.CommandLine.Command; -@Command(name = "top", description = "Top performing routes") -public class CamelTopStatus extends CamelRouteStatus { +@Command(name = "route", description = "Top performing routes") +public class CamelRouteTop extends CamelRouteStatus { - public CamelTopStatus(CamelJBangMain main) { + public CamelRouteTop(CamelJBangMain main) { super(main); } diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelTopStatus.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelTop.java similarity index 62% rename from dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelTopStatus.java rename to dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelTop.java index 44d6e8a17ea..93c56715adf 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelTopStatus.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/CamelTop.java @@ -16,28 +16,22 @@ */ package org.apache.camel.dsl.jbang.core.commands.process; +import org.apache.camel.dsl.jbang.core.commands.CamelCommand; import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; -import picocli.CommandLine.Command; +import picocli.CommandLine; -@Command(name = "top", description = "Top performing routes") -public class CamelTopStatus extends CamelRouteStatus { [email protected](name = "top", + description = "Top status of Camel integrations (use --help to see sub commands)") +public class CamelTop extends CamelCommand { - public CamelTopStatus(CamelJBangMain main) { + public CamelTop(CamelJBangMain main) { super(main); } @Override - protected int sortRow(Row o1, Row o2) { - // sort for highest mean value as we want the slowest in the top - long m1 = o1.mean != null ? Long.parseLong(o1.mean) : 0; - long m2 = o2.mean != null ? Long.parseLong(o2.mean) : 0; - if (m1 < m2) { - return 1; - } else if (m1 > m2) { - return -1; - } else { - return 0; - } + public Integer call() throws Exception { + // default to top the integrations + new CommandLine(new CamelRouteTop(getMain())).execute(); + return 0; } - }
