diff --git a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-activity.adoc b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-activity.adoc new file mode 100644 index 0000000000000..2d3bd89014b25 --- /dev/null +++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-activity.adoc @@ -0,0 +1,32 @@ + +// AUTO-GENERATED by camel-package-maven-plugin - DO NOT EDIT THIS FILE += camel get activity + +Get recent completed exchange activity + + +== Usage + +[source,bash] +---- +camel get activity [options] +---- + + + +== Options + +[cols="2,5,1,2",options="header"] +|=== +| Option | Description | Default | Type +| `--filter` | Filter activity by route ID | | String +| `--json` | Output in JSON Format | | boolean +| `--limit` | Filter activity by limiting to the given number of rows | | int +| `--short-uri` | List endpoint URI without query parameters (short) | | boolean +| `--sort` | Sort by pid, name, age, elapsed, or since | since | String +| `--watch` | Execute periodically and showing output fullscreen | | boolean +| `--wide-uri` | List endpoint URI in full details | | boolean +| `-h,--help` | Display the help and sub-commands | | boolean +|=== + + diff --git a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get.adoc b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get.adoc index 5365803b1101a..1e4f3601dfc95 100644 --- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get.adoc +++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get.adoc @@ -18,6 +18,7 @@ camel get [options] [cols="2,5",options="header"] |=== | Subcommand | Description +| xref:jbang-commands/camel-jbang-get-activity.adoc[activity] | Get recent completed exchange activity | xref:jbang-commands/camel-jbang-get-bean.adoc[bean] | List beans in a running Camel integration | xref:jbang-commands/camel-jbang-get-blocked.adoc[blocked] | Get blocked messages of Camel integrations | xref:jbang-commands/camel-jbang-get-circuit-breaker.adoc[circuit-breaker] | Get status of Circuit Breaker EIPs 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 b06303fceb469..4e8123d0d7678 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 @@ -130,6 +130,7 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C private File errorFile; private File receiveFile; private long receiveFilePos; // keep track of receive offset + private File activityFile; private byte[] lastSource; private ExpressionDefinition lastSourceExpression; @@ -202,6 +203,7 @@ protected void doStart() throws Exception { errorFile = createLockFile(lockFile.getName() + "-error.json"); debugFile = createLockFile(lockFile.getName() + "-debug.json"); receiveFile = createLockFile(lockFile.getName() + "-receive.json"); + activityFile = createLockFile(lockFile.getName() + "-activity.json"); scheduledFuture = executor.scheduleWithFixedDelay(this::task, 0, delay, TimeUnit.MILLISECONDS); LOG.info("Camel CLI connector enabled"); } else { @@ -1693,6 +1695,20 @@ protected void traceTask() { LOG.trace("Error updating receive file: {} due to: {}. This exception is ignored.", receiveFile, e.getMessage(), e); } + try { + DevConsole dc15 = camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class) + .resolveById("activity"); + if (dc15 != null) { + JsonObject json = (JsonObject) dc15.call(DevConsole.MediaType.JSON); + LOG.trace("Updating activity file: {}", activityFile); + String data = json.toJson() + System.lineSeparator(); + IOHelper.writeText(data, activityFile); + } + } catch (Exception e) { + // ignore + LOG.trace("Error updating activity file: {} due to: {}. This exception is ignored.", + activityFile, e.getMessage(), e); + } } private JsonObject collectMemory() { @@ -1853,6 +1869,9 @@ protected void doStop() throws Exception { if (receiveFile != null) { FileUtil.deleteFile(receiveFile); } + if (activityFile != null) { + FileUtil.deleteFile(activityFile); + } if (executor != null) { camelContext.getExecutorServiceManager().shutdown(executor); executor = null; diff --git a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json index 92d22a2b1b27a..0925154396672 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json +++ b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json @@ -15,7 +15,7 @@ { "name": "eval", "fullName": "eval", "description": "Evaluate Camel expressions and scripts", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.EvalCommand", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "expression", "fullName": "eval expression", "description": "Evaluates Camel expression", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.EvalExpressionCommand", "options": [ { "names": "--body", "description": "Message body (prefix with file: to refer to loading message body from file)", "javaType": "java.lang.String", "type": "string" }, { "names": "--camel-version", "description": "To run using a different Camel version than the default version.", "javaType": "java.lang.String", "type": "string" }, { "names": "--download", "description": "Whether to allow automatic downloading JAR dependencies (over the internet)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--fresh", "description": "Make sure we use fresh (i.e. non-cached) resources", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--header", "description": "Message header (key=value)", "javaType": "java.util.List", "type": "array" }, { "names": "--isolated", "description": "Whether to run evaluation isolated in local process", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--language", "description": "Language to use", "defaultValue": "simple", "javaType": "java.lang.String", "type": "string" }, { "names": "--predicate", "description": "Whether to force evaluating as predicate", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--repo,--repos", "description": "Additional maven repositories for download on-demand (Use commas to separate multiple repositories)", "javaType": "java.lang.String", "type": "string" }, { "names": "--template", "description": "The template to use for evaluating (prefix with file: to refer to loading template from file)", "javaType": "java.lang.String", "type": "string", "required": true }, { "names": "--timeout", "description": "Timeout in millis waiting for evaluation to be done", "defaultValue": "10000", "javaType": "long", "type": "integer" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] } ] }, { "name": "explain", "fullName": "explain", "description": "Explain what a Camel route does using AI\/LLM", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Explain", "options": [ { "names": "--api-key", "description": "API key for authentication. Also reads ANTHROPIC_API_KEY, OPENAI_API_KEY, or LLM_API_KEY env vars", "javaType": "java.lang.String", "type": "string" }, { "names": "--api-type", "description": "API type: 'ollama', 'openai' (OpenAI-compatible), or 'anthropic' (Anthropic\/Vertex AI)", "javaType": "LlmClient.ApiType", "type": "object" }, { "names": "--catalog-context", "description": "Include Camel Catalog descriptions in the prompt", "javaType": "boolean", "type": "boolean" }, { "names": "--format", "description": "Output format (text, markdown)", "defaultValue": "text", "javaType": "java.lang.String", "type": "string" }, { "names": "--model", "description": "Model to use", "defaultValue": "DEFAULT_MODEL", "javaType": "java.lang.String", "type": "string" }, { "names": "--show-prompt", "description": "Show the prompt sent to the LLM", "javaType": "boolean", "type": "boolean" }, { "names": "--stream", "description": "Stream the response as it's generated (shows progress)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--system-prompt", "description": "Custom system prompt", "javaType": "java.lang.String", "type": "string" }, { "names": "--temperature", "description": "Temperature for response generation (0.0-2.0)", "defaultValue": "0.7", "javaType": "double", "type": "number" }, { "names": "--timeout", "description": "Timeout in seconds for LLM response", "defaultValue": "120", "javaType": "int", "type": "integer" }, { "names": "--url", "description": "LLM API endpoint URL. Auto-detected from 'camel infra' for Ollama if not specified.", "javaType": "java.lang.String", "type": "string" }, { "names": "--verbose,-v", "description": "Include detailed technical information", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "export", "fullName": "export", "description": "Export to other runtimes (Camel Main, Spring Boot, or Quarkus)", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Export", "options": [ { "names": "--build-property", "description": "Maven build properties, ex. --build-property=prop1=foo", "javaType": "java.util.List", "type": "array" }, { "names": "--camel-spring-boot-version", "description": "Camel version to use with Spring Boot", "javaType": "java.lang.String", "type": "string" }, { "names": "--camel-version", "description": "To export using a different Camel version than the default version.", "javaType": "java.lang.String", "type": "string" }, { "names": "--clean-dir", "description": "If exporting to current directory (default) then all existing files are preserved. Enabling this option will force cleaning current directory including all sub dirs (use this with care)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--dep,--dependency", "description": "Add additional dependencies", "javaType": "java.util.List", "type": "array" }, { "names": "--dir,--directory", "description": "Directory where the project will be exported", "defaultValue": ".", "javaType": "java.lang.String", "type": "string" }, { "names": "--docker", "description": "Include Docker files in exported project", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--download", "description": "Whether to allow automatic downloading JAR dependencies (over the internet)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--dry-run", "description": "Preview export without writing files", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--exclude", "description": "Exclude files by name or pattern", "javaType": "java.util.List", "type": "array" }, { "names": "--fresh", "description": "Make sure we use fresh (i.e. non-cached) resources", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--gav", "description": "The Maven group:artifact:version", "javaType": "java.lang.String", "type": "string" }, { "names": "--groovy-pre-compiled", "description": "Whether to include pre-compiled Groovy classes in the export (only supported with runtime=camel-main)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--hawtio", "description": "Whether to include Hawtio web console (only available for exporting to Spring Boot or Quarkus)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--hawtio-version", "description": "Version of the Hawtio web console", "defaultValue": "HawtioVersion.HAWTIO_VERSION", "javaType": "java.lang.String", "type": "string" }, { "names": "--ignore-loading-error", "description": "Whether to ignore route loading and compilation errors (use this with care!)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--java-version,--java", "description": "Java version (21, 25)", "defaultValue": "21", "javaType": "java.lang.String", "type": "string" }, { "names": "--kamelets-version", "description": "Apache Camel Kamelets version (auto-detected from classpath if not set)", "javaType": "java.lang.String", "type": "string" }, { "names": "--lazy-bean", "description": "Whether to use lazy bean initialization (can help with complex classloading issues", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--local-kamelet-dir", "description": "Local directory for loading Kamelets (takes precedence)", "javaType": "java.lang.String", "type": "string" }, { "names": "--logging", "description": "Can be used to turn on logging to console (logs by default to file in \/.camel directory)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--logging-level", "description": "Logging level (ERROR, WARN, INFO, DEBUG, TRACE)", "defaultValue": "info", "javaType": "java.lang.String", "type": "string" }, { "names": "--main-classname", "description": "The class name of the Camel Main application class", "defaultValue": "CamelApplication", "javaType": "java.lang.String", "type": "string" }, { "names": "--management-port", "description": "To use a dedicated port for HTTP management", "javaType": "int", "type": "integer" }, { "names": "--maven-apache-snapshot-enabled", "description": "Whether downloading JARs from ASF Maven Snapshot repository is enabled", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--maven-central-enabled", "description": "Whether downloading JARs from Maven Central repository is enabled", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--maven-settings", "description": "Optional location of Maven settings.xml file to configure servers, repositories, mirrors and proxies. If set to false, not even the default ~\/.m2\/settings.xml will be used.", "javaType": "java.lang.String", "type": "string" }, { "names": "--maven-settings-security", "description": "Optional location of Maven settings-security.xml file to decrypt settings.xml", "javaType": "java.lang.String", "type": "string" }, { "names": "--maven-wrapper", "description": "Include Maven Wrapper files in exported project", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--name", "description": "The integration name. Use this when the name should not get derived otherwise.", "javaType": "java.lang.String", "type": "string" }, { "names": "--observe", "description": "Enable observability services", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--open-api", "description": "Adds an OpenAPI spec from the given file (json or yaml file)", "javaType": "java.lang.String", "type": "string" }, { "names": "--package-name", "description": "For Java source files should they have the given package name. By default the package name is computed from the Maven GAV. Use false to turn off and not include package name in the Java source files.", "javaType": "java.lang.String", "type": "string" }, { "names": "--package-scan-jars", "description": "Whether to automatic package scan JARs for custom Spring or Quarkus beans making them available for Camel CLI", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--port", "description": "Embeds a local HTTP server on this port", "javaType": "int", "type": "integer" }, { "names": "--profile", "description": "Profile to export (dev, test, prod).", "javaType": "java.lang.String", "type": "string" }, { "names": "--prop,--property", "description": "Camel application properties, ex. --property=prop1=foo", "javaType": "java.lang.String", "type": "string" }, { "names": "--quarkus-artifact-id", "description": "Deprecated. This value is not used anymore. It is kept only for backwards compatibility and will be removed in Camel 5.x. Camel commands may use either 'quarkus-bom' or 'quarkus-camel-bom' artifactIds depending on the context.", "defaultValue": "quarkus-bom", "javaType": "java.lang.String", "type": "string", "deprecated": true }, { "names": "--quarkus-ext-registry", "description": "The base URI of Quarkus Extension Registry. The default is {@value RuntimeType#QUARKUS_EXTENSION_REGISTRY_BASE_URL} unless camel.jbang.quarkus.platform.url system property is set (the \/client\/platforms suffix is removed if present).", "javaType": "java.lang.String", "type": "string" }, { "names": "--quarkus-group-id", "description": "groupId of Quarkus Platform BOM; honored only if --quarkus-version is set", "defaultValue": "io.quarkus.platform", "javaType": "java.lang.String", "type": "string" }, { "names": "--quarkus-version", "description": "version of Quarkus Platform BOM; the default value is looked up in Quarkus Extension Registry", "javaType": "java.lang.String", "type": "string" }, { "names": "--quiet", "description": "Will be quiet, only print when error occurs", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--repo,--repos", "description": "Additional maven repositories for download on-demand (Use commas to separate multiple repositories)", "javaType": "java.lang.String", "type": "string" }, { "names": "--runtime", "description": "Runtime (camel-main, spring-boot, quarkus)", "javaType": "org.apache.camel.dsl.jbang.core.common.RuntimeType", "type": "object" }, { "names": "--skip-plugins", "description": "Skip plugins during export", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--spring-boot-version", "description": "Spring Boot version", "defaultValue": "RuntimeType.SPRING_BOOT_VERSION", "javaType": "java.lang.String", "type": "string" }, { "names": "--verbose", "description": "Verbose output of startup activity (dependency resolution and downloading", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--yes,-y", "description": "Automatically answer yes to confirmation prompts (e.g. when using --clean-dir)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, - { "name": "get", "fullName": "get", "description": "Get status of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.CamelStatus", "options": [ { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "bean", "fullName": "get bean", "description": "List beans in a running Camel integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelBeanDump", "options": [ { "names": "--dsl", "description": "Include only beans from YAML or XML DSL", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--filter", "description": "Filter beans names (use all to include all beans)", "defaultValue": "all", "javaType": "java.lang.String", "type": "string" }, { "names": "--internal", "description": "Include internal Camel beans", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--nulls", "description": "Include null values", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--properties", "description": "Show bean properties", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--scope", "description": "Filter beans by scope: all, user (excludes Camel\/Spring\/Quarkus\/JDK), camel, spring, quarkus", "defaultValue": "all", "javaType": "java.lang.String", "type": "string" }, { "names": "--sort", "description": "Sort by name or type", "defaultValue": "name", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "blocked", "fullName": "get blocked", "description": "Get blocked messages of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListBlocked", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "circuit-breaker", "fullName": "get circuit-breaker", "description": "Get status of Circuit Breaker EIPs", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListCircuitBreaker", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "consumer", "fullName": "get consumer", "description": "Get status of Camel consumers", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListConsumer", "options": [ { "names": "--filter", "description": "Filter consumers by URI", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--limit", "description": "Filter consumers by limiting to the given number of rows", "javaType": "int", "type": "integer" }, { "names": "--scheduled", "description": "Filter consumer to only show scheduled based consumers", "javaType": "boolean", "type": "boolean" }, { "names": "--short-uri", "description": "List endpoint URI without query parameters (short)", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "--wide-uri", "description": "List endpoint URI in full details", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "context", "fullName": "get context", "description": "Get status of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.CamelContextStatus", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--remote", "description": "Break down counters into remote\/total pairs", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "count", "fullName": "get count", "description": "Get total and failed exchanges", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.CamelCount", "options": [ { "names": "--fail", "description": "Get the failed exchanges from a running integration", "javaType": "boolean", "type": "boolean" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--total", "description": "Get the total exchanges from a running integration", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "datasource", "fullName": "get datasource", "description": "Get status of DataSource connection pools", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListDataSource", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "endpoint", "fullName": "get endpoint", "description": "Get usage of Camel endpoints", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListEndpoint", "options": [ { "names": "--filter", "description": "Filter endpoints by URI", "javaType": "java.lang.String", "type": "string" }, { "names": "--filter-direction", "description": "Filter by direction (in or out)", "javaType": "java.lang.String", "type": "string" }, { "names": "--filter-total", "description": "Filter endpoints that must be higher than the given usage", "javaType": "long", "type": "integer" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--limit", "description": "Filter endpoints by limiting to the given number of rows", "javaType": "int", "type": "integer" }, { "names": "--short-uri", "description": "List endpoint URI without query parameters (short)", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name, age, total, or size", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--verbose", "description": "Show additional size statistics (min\/max body and headers)", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "--wide-uri", "description": "List endpoint URI in full details", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "error", "fullName": "get error", "description": "Get captured routing errors of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListError", "options": [ { "names": "--ago", "description": "Filter by time window, e.g. 60s, 5m, 1h", "javaType": "java.lang.String", "type": "string" }, { "names": "--detail", "description": "Show full details of each error entry", "javaType": "boolean", "type": "boolean" }, { "names": "--diagram", "description": "Display a route diagram with the error path highlighted", "javaType": "boolean", "type": "boolean" }, { "names": "--exception", "description": "Filter by exception type (substring match)", "javaType": "java.lang.String", "type": "string" }, { "names": "--handled", "description": "Filter by handled status (true or false)", "javaType": "java.lang.String", "type": "string" }, { "names": "--id", "description": "Filter by exchange ID", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--last", "description": "Show only the last (newest) error with full details", "javaType": "boolean", "type": "boolean" }, { "names": "--limit", "description": "Maximum number of entries to display", "javaType": "int", "type": "integer" }, { "names": "--logging-color", "description": "Use colored logging", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--route", "description": "Filter by route ID", "javaType": "java.lang.String", "type": "string" }, { "names": "--show", "description": "Comma-separated detail sections to show: body, headers, properties, variables, history, stackTrace, or 'all' for all sections", "javaType": "java.lang.String", "type": "string" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--theme", "description": "Diagram color theme (ascii, unicode, dark, light, transparent, or custom)", "defaultValue": "unicode", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "event", "fullName": "get event", "description": "Get latest events of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListEvent", "options": [ { "names": "--filter", "description": "Filter event by event type: context, route, or exchange", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "groovy", "fullName": "get groovy", "description": "Groovy Sources used of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListGroovy", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid or name", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "group", "fullName": "get group", "description": "Get status of Camel route groups", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.CamelRouteGroupStatus", "options": [ { "names": "--filter", "description": "Filter groups by name", "javaType": "java.lang.String", "type": "string" }, { "names": "--filter-mean", "description": "Filter groups that must be slower than the given time (ms)", "javaType": "long", "type": "integer" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--limit", "description": "Filter groups by limiting to the given number of rows", "javaType": "int", "type": "integer" }, { "names": "--running", "description": "Only include running groups", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name, age or group", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "health", "fullName": "get health", "description": "Get health check status of running Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListHealth", "options": [ { "names": "--depth", "description": "Max depth of stack-trace", "defaultValue": "1", "javaType": "int", "type": "integer" }, { "names": "--down", "description": "Show only checks which are DOWN", "javaType": "boolean", "type": "boolean" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--level", "description": "Level of details: full, or default", "defaultValue": "default", "javaType": "java.lang.String", "type": "string" }, { "names": "--live", "description": "Show only liveness checks", "javaType": "boolean", "type": "boolean" }, { "names": "--ready", "description": "Show only readiness checks", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--trace", "description": "Include stack-traces in error messages", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "history", "fullName": "get history", "description": "History of latest completed exchange", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelHistoryAction", "options": [ { "names": "--ago", "description": "Use ago instead of yyyy-MM-dd HH:mm:ss in timestamp.", "javaType": "boolean", "type": "boolean" }, { "names": "--depth", "description": "Depth of tracing. 0=Created Completed. 1=All events on 1st route, 2=All events on 1st 2nd depth, and so on. 9 = all events on every depth.", "defaultValue": "9", "javaType": "int", "type": "integer" }, { "names": "--diagram", "description": "Display a route diagram with the message path highlighted", "javaType": "boolean", "type": "boolean" }, { "names": "--it", "description": "Interactive mode for enhanced history information", "javaType": "boolean", "type": "boolean" }, { "names": "--limit-split", "description": "Limit Split to a maximum number of entries to be displayed", "javaType": "int", "type": "integer" }, { "names": "--logging-color", "description": "Use colored logging", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--mask", "description": "Whether to mask endpoint URIs to avoid printing sensitive information such as password or access keys", "javaType": "boolean", "type": "boolean" }, { "names": "--pretty", "description": "Pretty print message body when using JSon or XML format", "javaType": "boolean", "type": "boolean" }, { "names": "--show-body", "description": "Show message body in debug messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--show-exception", "description": "Show exception and stacktrace for failed messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--show-exchange-properties", "description": "Show exchange properties in debug messages", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--show-exchange-variables", "description": "Show exchange variables in debug messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--show-headers", "description": "Show message headers in debug messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--source", "description": "Prefer to display source filename\/code instead of IDs", "javaType": "boolean", "type": "boolean" }, { "names": "--theme", "description": "Diagram color theme (ascii, unicode, dark, light, transparent, or custom)", "defaultValue": "unicode", "javaType": "java.lang.String", "type": "string" }, { "names": "--timestamp", "description": "Print timestamp.", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "inflight", "fullName": "get inflight", "description": "Get inflight messages of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListInflight", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "internal-task", "fullName": "get internal-task", "description": "List internal tasks of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListInternalTask", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "kafka", "fullName": "get kafka", "description": "List Kafka consumers of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListKafka", "options": [ { "names": "--committed", "description": "Show committed offset (slower due to sync call to Kafka brokers)", "javaType": "boolean", "type": "boolean" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--short-uri", "description": "List endpoint URI without query parameters (short)", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "--wide-uri", "description": "List endpoint URI in full details", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "metric", "fullName": "get metric", "description": "Get metrics (micrometer) of running Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListMetric", "options": [ { "names": "--all", "description": "Whether to show all metrics (also unused with counter being 0)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--custom", "description": "Only show custom metrics", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--filter", "description": "Filter metric by type, name or tags", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--tags", "description": "Show metric tags", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "platform-http", "fullName": "get platform-http", "description": "Get embedded HTTP services of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListPlatformHttp", "options": [ { "names": "--all", "description": "Include management endpoints", "javaType": "boolean", "type": "boolean" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "processor", "fullName": "get processor", "description": "Get status of Camel processors", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.CamelProcessorStatus", "options": [ { "names": "--description", "description": "Include description in the ID column (if available)", "javaType": "boolean", "type": "boolean" }, { "names": "--filter", "description": "Filter processors by id", "javaType": "java.lang.String", "type": "string" }, { "names": "--filter-mean", "description": "Filter processors that must be slower than the given time (ms)", "javaType": "long", "type": "integer" }, { "names": "--group", "description": "Filter processors by group", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--limit", "description": "Filter routes by limiting to the given number of rows", "javaType": "int", "type": "integer" }, { "names": "--note", "description": "Include note in the ID column (if available)", "javaType": "boolean", "type": "boolean" }, { "names": "--remote", "description": "Break down counters into remote\/total pairs", "javaType": "boolean", "type": "boolean" }, { "names": "--running", "description": "Only include running processors", "javaType": "boolean", "type": "boolean" }, { "names": "--show-group", "description": "Include group column", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid or name", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--source", "description": "Prefer to display source filename\/code instead of IDs", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "producer", "fullName": "get producer", "description": "Get status of Camel producers", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListProducer", "options": [ { "names": "--filter", "description": "Filter producers by URI", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--limit", "description": "Filter producers by limiting to the given number of rows", "javaType": "int", "type": "integer" }, { "names": "--short-uri", "description": "List endpoint URI without query parameters (short)", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "--wide-uri", "description": "List endpoint URI in full details", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "properties", "fullName": "get properties", "description": "List configuration properties", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListProperties", "options": [ { "names": "--internal", "description": "Whether to include internal configuration", "javaType": "boolean", "type": "boolean" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--mask", "description": "Whether to mask configuration values to avoid printing sensitive information such as password or access keys", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or key", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--startup", "description": "List only startup configuration", "javaType": "boolean", "type": "boolean" }, { "names": "--verbose", "description": "Whether to include more details", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "rest", "fullName": "get rest", "description": "Get REST services of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListRest", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--verbose", "description": "Show more details", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "route", "fullName": "get route", "description": "Get status of Camel routes", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.CamelRouteStatus", "options": [ { "names": "--description", "description": "Include description in the ID column (if available)", "javaType": "boolean", "type": "boolean" }, { "names": "--error", "description": "Shows detailed information for routes that has error status", "javaType": "boolean", "type": "boolean" }, { "names": "--filter", "description": "Filter routes by id, or url", "javaType": "java.lang.String", "type": "string" }, { "names": "--filter-mean", "description": "Filter routes that must be slower than the given time (ms)", "javaType": "long", "type": "integer" }, { "names": "--group", "description": "Filter routes by group", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--limit", "description": "Filter routes by limiting to the given number of rows", "javaType": "int", "type": "integer" }, { "names": "--note", "description": "Include note in the ID column (if available)", "javaType": "boolean", "type": "boolean" }, { "names": "--running", "description": "Only include running routes", "javaType": "boolean", "type": "boolean" }, { "names": "--short-uri", "description": "List endpoint URI without query parameters (short)", "javaType": "boolean", "type": "boolean" }, { "names": "--show-group", "description": "Include group column", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--source", "description": "Prefer to display source filename\/code instead of IDs", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "--wide-uri", "description": "List endpoint URI in full details", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "route-controller", "fullName": "get route-controller", "description": "List status of route controller", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.RouteControllerAction", "options": [ { "names": "--depth", "description": "Max depth of stack-trace", "defaultValue": "1", "javaType": "int", "type": "integer" }, { "names": "--header", "description": "Include controller configuration details", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by id, or state", "defaultValue": "id", "javaType": "java.lang.String", "type": "string" }, { "names": "--trace", "description": "Include stack-traces in error messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "service", "fullName": "get service", "description": "Get services of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListService", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--metadata", "description": "Show service metadata (only available for some services)", "javaType": "boolean", "type": "boolean" }, { "names": "--short-uri", "description": "List endpoint URI without query parameters (short)", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "--wide-uri", "description": "List endpoint URI in full details", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "source", "fullName": "get source", "description": "Display Camel route source code", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelSourceAction", "options": [ { "names": "--filter", "description": "Filter source by filename (multiple names can be separated by comma)", "javaType": "java.lang.String", "type": "string" }, { "names": "--sort", "description": "Sort source by name", "defaultValue": "name", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "sql-trace", "fullName": "get sql-trace", "description": "Get SQL query trace data", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListSqlTrace", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "startup-recorder", "fullName": "get startup-recorder", "description": "Display startup recording", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelStartupRecorderAction", "options": [ { "names": "--sort", "description": "Sort by duration, or type", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "transformer", "fullName": "get transformer", "description": "Get list of data type transformers", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListTransformer", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name, age or total", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "variable", "fullName": "get variable", "description": "List variables of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListVariable", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or key", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "vault", "fullName": "get vault", "description": "List secrets from security vaults", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListVault", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] } ] }, + { "name": "get", "fullName": "get", "description": "Get status of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.CamelStatus", "options": [ { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "activity", "fullName": "get activity", "description": "Get recent completed exchange activity", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListActivity", "options": [ { "names": "--filter", "description": "Filter activity by route ID", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--limit", "description": "Filter activity by limiting to the given number of rows", "javaType": "int", "type": "integer" }, { "names": "--short-uri", "description": "List endpoint URI without query parameters (short)", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name, age, elapsed, or since", "defaultValue": "since", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "--wide-uri", "description": "List endpoint URI in full details", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "bean", "fullName": "get bean", "description": "List beans in a running Camel integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelBeanDump", "options": [ { "names": "--dsl", "description": "Include only beans from YAML or XML DSL", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--filter", "description": "Filter beans names (use all to include all beans)", "defaultValue": "all", "javaType": "java.lang.String", "type": "string" }, { "names": "--internal", "description": "Include internal Camel beans", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--nulls", "description": "Include null values", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--properties", "description": "Show bean properties", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--scope", "description": "Filter beans by scope: all, user (excludes Camel\/Spring\/Quarkus\/JDK), camel, spring, quarkus", "defaultValue": "all", "javaType": "java.lang.String", "type": "string" }, { "names": "--sort", "description": "Sort by name or type", "defaultValue": "name", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "blocked", "fullName": "get blocked", "description": "Get blocked messages of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListBlocked", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "circuit-breaker", "fullName": "get circuit-breaker", "description": "Get status of Circuit Breaker EIPs", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListCircuitBreaker", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "consumer", "fullName": "get consumer", "description": "Get status of Camel consumers", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListConsumer", "options": [ { "names": "--filter", "description": "Filter consumers by URI", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--limit", "description": "Filter consumers by limiting to the given number of rows", "javaType": "int", "type": "integer" }, { "names": "--scheduled", "description": "Filter consumer to only show scheduled based consumers", "javaType": "boolean", "type": "boolean" }, { "names": "--short-uri", "description": "List endpoint URI without query parameters (short)", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "--wide-uri", "description": "List endpoint URI in full details", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "context", "fullName": "get context", "description": "Get status of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.CamelContextStatus", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--remote", "description": "Break down counters into remote\/total pairs", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "count", "fullName": "get count", "description": "Get total and failed exchanges", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.CamelCount", "options": [ { "names": "--fail", "description": "Get the failed exchanges from a running integration", "javaType": "boolean", "type": "boolean" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--total", "description": "Get the total exchanges from a running integration", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "datasource", "fullName": "get datasource", "description": "Get status of DataSource connection pools", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListDataSource", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "endpoint", "fullName": "get endpoint", "description": "Get usage of Camel endpoints", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListEndpoint", "options": [ { "names": "--filter", "description": "Filter endpoints by URI", "javaType": "java.lang.String", "type": "string" }, { "names": "--filter-direction", "description": "Filter by direction (in or out)", "javaType": "java.lang.String", "type": "string" }, { "names": "--filter-total", "description": "Filter endpoints that must be higher than the given usage", "javaType": "long", "type": "integer" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--limit", "description": "Filter endpoints by limiting to the given number of rows", "javaType": "int", "type": "integer" }, { "names": "--short-uri", "description": "List endpoint URI without query parameters (short)", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name, age, total, or size", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--verbose", "description": "Show additional size statistics (min\/max body and headers)", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "--wide-uri", "description": "List endpoint URI in full details", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "error", "fullName": "get error", "description": "Get captured routing errors of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListError", "options": [ { "names": "--ago", "description": "Filter by time window, e.g. 60s, 5m, 1h", "javaType": "java.lang.String", "type": "string" }, { "names": "--detail", "description": "Show full details of each error entry", "javaType": "boolean", "type": "boolean" }, { "names": "--diagram", "description": "Display a route diagram with the error path highlighted", "javaType": "boolean", "type": "boolean" }, { "names": "--exception", "description": "Filter by exception type (substring match)", "javaType": "java.lang.String", "type": "string" }, { "names": "--handled", "description": "Filter by handled status (true or false)", "javaType": "java.lang.String", "type": "string" }, { "names": "--id", "description": "Filter by exchange ID", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--last", "description": "Show only the last (newest) error with full details", "javaType": "boolean", "type": "boolean" }, { "names": "--limit", "description": "Maximum number of entries to display", "javaType": "int", "type": "integer" }, { "names": "--logging-color", "description": "Use colored logging", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--route", "description": "Filter by route ID", "javaType": "java.lang.String", "type": "string" }, { "names": "--show", "description": "Comma-separated detail sections to show: body, headers, properties, variables, history, stackTrace, or 'all' for all sections", "javaType": "java.lang.String", "type": "string" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--theme", "description": "Diagram color theme (ascii, unicode, dark, light, transparent, or custom)", "defaultValue": "unicode", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "event", "fullName": "get event", "description": "Get latest events of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListEvent", "options": [ { "names": "--filter", "description": "Filter event by event type: context, route, or exchange", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "groovy", "fullName": "get groovy", "description": "Groovy Sources used of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListGroovy", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid or name", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "group", "fullName": "get group", "description": "Get status of Camel route groups", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.CamelRouteGroupStatus", "options": [ { "names": "--filter", "description": "Filter groups by name", "javaType": "java.lang.String", "type": "string" }, { "names": "--filter-mean", "description": "Filter groups that must be slower than the given time (ms)", "javaType": "long", "type": "integer" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--limit", "description": "Filter groups by limiting to the given number of rows", "javaType": "int", "type": "integer" }, { "names": "--running", "description": "Only include running groups", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name, age or group", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "health", "fullName": "get health", "description": "Get health check status of running Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListHealth", "options": [ { "names": "--depth", "description": "Max depth of stack-trace", "defaultValue": "1", "javaType": "int", "type": "integer" }, { "names": "--down", "description": "Show only checks which are DOWN", "javaType": "boolean", "type": "boolean" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--level", "description": "Level of details: full, or default", "defaultValue": "default", "javaType": "java.lang.String", "type": "string" }, { "names": "--live", "description": "Show only liveness checks", "javaType": "boolean", "type": "boolean" }, { "names": "--ready", "description": "Show only readiness checks", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--trace", "description": "Include stack-traces in error messages", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "history", "fullName": "get history", "description": "History of latest completed exchange", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelHistoryAction", "options": [ { "names": "--ago", "description": "Use ago instead of yyyy-MM-dd HH:mm:ss in timestamp.", "javaType": "boolean", "type": "boolean" }, { "names": "--depth", "description": "Depth of tracing. 0=Created Completed. 1=All events on 1st route, 2=All events on 1st 2nd depth, and so on. 9 = all events on every depth.", "defaultValue": "9", "javaType": "int", "type": "integer" }, { "names": "--diagram", "description": "Display a route diagram with the message path highlighted", "javaType": "boolean", "type": "boolean" }, { "names": "--it", "description": "Interactive mode for enhanced history information", "javaType": "boolean", "type": "boolean" }, { "names": "--limit-split", "description": "Limit Split to a maximum number of entries to be displayed", "javaType": "int", "type": "integer" }, { "names": "--logging-color", "description": "Use colored logging", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--mask", "description": "Whether to mask endpoint URIs to avoid printing sensitive information such as password or access keys", "javaType": "boolean", "type": "boolean" }, { "names": "--pretty", "description": "Pretty print message body when using JSon or XML format", "javaType": "boolean", "type": "boolean" }, { "names": "--show-body", "description": "Show message body in debug messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--show-exception", "description": "Show exception and stacktrace for failed messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--show-exchange-properties", "description": "Show exchange properties in debug messages", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--show-exchange-variables", "description": "Show exchange variables in debug messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--show-headers", "description": "Show message headers in debug messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--source", "description": "Prefer to display source filename\/code instead of IDs", "javaType": "boolean", "type": "boolean" }, { "names": "--theme", "description": "Diagram color theme (ascii, unicode, dark, light, transparent, or custom)", "defaultValue": "unicode", "javaType": "java.lang.String", "type": "string" }, { "names": "--timestamp", "description": "Print timestamp.", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "inflight", "fullName": "get inflight", "description": "Get inflight messages of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListInflight", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "internal-task", "fullName": "get internal-task", "description": "List internal tasks of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListInternalTask", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "kafka", "fullName": "get kafka", "description": "List Kafka consumers of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListKafka", "options": [ { "names": "--committed", "description": "Show committed offset (slower due to sync call to Kafka brokers)", "javaType": "boolean", "type": "boolean" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--short-uri", "description": "List endpoint URI without query parameters (short)", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "--wide-uri", "description": "List endpoint URI in full details", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "metric", "fullName": "get metric", "description": "Get metrics (micrometer) of running Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListMetric", "options": [ { "names": "--all", "description": "Whether to show all metrics (also unused with counter being 0)", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--custom", "description": "Only show custom metrics", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--filter", "description": "Filter metric by type, name or tags", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--tags", "description": "Show metric tags", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "platform-http", "fullName": "get platform-http", "description": "Get embedded HTTP services of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListPlatformHttp", "options": [ { "names": "--all", "description": "Include management endpoints", "javaType": "boolean", "type": "boolean" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "processor", "fullName": "get processor", "description": "Get status of Camel processors", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.CamelProcessorStatus", "options": [ { "names": "--description", "description": "Include description in the ID column (if available)", "javaType": "boolean", "type": "boolean" }, { "names": "--filter", "description": "Filter processors by id", "javaType": "java.lang.String", "type": "string" }, { "names": "--filter-mean", "description": "Filter processors that must be slower than the given time (ms)", "javaType": "long", "type": "integer" }, { "names": "--group", "description": "Filter processors by group", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--limit", "description": "Filter routes by limiting to the given number of rows", "javaType": "int", "type": "integer" }, { "names": "--note", "description": "Include note in the ID column (if available)", "javaType": "boolean", "type": "boolean" }, { "names": "--remote", "description": "Break down counters into remote\/total pairs", "javaType": "boolean", "type": "boolean" }, { "names": "--running", "description": "Only include running processors", "javaType": "boolean", "type": "boolean" }, { "names": "--show-group", "description": "Include group column", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid or name", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--source", "description": "Prefer to display source filename\/code instead of IDs", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "producer", "fullName": "get producer", "description": "Get status of Camel producers", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListProducer", "options": [ { "names": "--filter", "description": "Filter producers by URI", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--limit", "description": "Filter producers by limiting to the given number of rows", "javaType": "int", "type": "integer" }, { "names": "--short-uri", "description": "List endpoint URI without query parameters (short)", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "--wide-uri", "description": "List endpoint URI in full details", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "properties", "fullName": "get properties", "description": "List configuration properties", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListProperties", "options": [ { "names": "--internal", "description": "Whether to include internal configuration", "javaType": "boolean", "type": "boolean" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--mask", "description": "Whether to mask configuration values to avoid printing sensitive information such as password or access keys", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or key", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--startup", "description": "List only startup configuration", "javaType": "boolean", "type": "boolean" }, { "names": "--verbose", "description": "Whether to include more details", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "rest", "fullName": "get rest", "description": "Get REST services of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListRest", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--verbose", "description": "Show more details", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "route", "fullName": "get route", "description": "Get status of Camel routes", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.CamelRouteStatus", "options": [ { "names": "--description", "description": "Include description in the ID column (if available)", "javaType": "boolean", "type": "boolean" }, { "names": "--error", "description": "Shows detailed information for routes that has error status", "javaType": "boolean", "type": "boolean" }, { "names": "--filter", "description": "Filter routes by id, or url", "javaType": "java.lang.String", "type": "string" }, { "names": "--filter-mean", "description": "Filter routes that must be slower than the given time (ms)", "javaType": "long", "type": "integer" }, { "names": "--group", "description": "Filter routes by group", "javaType": "java.lang.String", "type": "string" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--limit", "description": "Filter routes by limiting to the given number of rows", "javaType": "int", "type": "integer" }, { "names": "--note", "description": "Include note in the ID column (if available)", "javaType": "boolean", "type": "boolean" }, { "names": "--running", "description": "Only include running routes", "javaType": "boolean", "type": "boolean" }, { "names": "--short-uri", "description": "List endpoint URI without query parameters (short)", "javaType": "boolean", "type": "boolean" }, { "names": "--show-group", "description": "Include group column", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--source", "description": "Prefer to display source filename\/code instead of IDs", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "--wide-uri", "description": "List endpoint URI in full details", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "route-controller", "fullName": "get route-controller", "description": "List status of route controller", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.RouteControllerAction", "options": [ { "names": "--depth", "description": "Max depth of stack-trace", "defaultValue": "1", "javaType": "int", "type": "integer" }, { "names": "--header", "description": "Include controller configuration details", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by id, or state", "defaultValue": "id", "javaType": "java.lang.String", "type": "string" }, { "names": "--trace", "description": "Include stack-traces in error messages", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "service", "fullName": "get service", "description": "Get services of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListService", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--metadata", "description": "Show service metadata (only available for some services)", "javaType": "boolean", "type": "boolean" }, { "names": "--short-uri", "description": "List endpoint URI without query parameters (short)", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "--wide-uri", "description": "List endpoint URI in full details", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "source", "fullName": "get source", "description": "Display Camel route source code", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelSourceAction", "options": [ { "names": "--filter", "description": "Filter source by filename (multiple names can be separated by comma)", "javaType": "java.lang.String", "type": "string" }, { "names": "--sort", "description": "Sort source by name", "defaultValue": "name", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "sql-trace", "fullName": "get sql-trace", "description": "Get SQL query trace data", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListSqlTrace", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or age", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "startup-recorder", "fullName": "get startup-recorder", "description": "Display startup recording", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.CamelStartupRecorderAction", "options": [ { "names": "--sort", "description": "Sort by duration, or type", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "transformer", "fullName": "get transformer", "description": "Get list of data type transformers", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListTransformer", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name, age or total", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "variable", "fullName": "get variable", "description": "List variables of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListVariable", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name or key", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "vault", "fullName": "get vault", "description": "List secrets from security vaults", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.ListVault", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--sort", "description": "Sort by pid, name", "defaultValue": "pid", "javaType": "java.lang.String", "type": "string" }, { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] } ] }, { "name": "harden", "fullName": "harden", "description": "Suggest security hardening for Camel routes using AI\/LLM", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Harden", "options": [ { "names": "--api-key", "description": "API key for authentication. Also reads OPENAI_API_KEY or LLM_API_KEY env vars", "javaType": "java.lang.String", "type": "string" }, { "names": "--api-type", "description": "API type: 'ollama' or 'openai' (OpenAI-compatible)", "defaultValue": "ollama", "javaType": "org.apache.camel.dsl.jbang.core.commands.ApiType", "type": "object" }, { "names": "--catalog-context", "description": "Include Camel Catalog descriptions in the prompt", "javaType": "boolean", "type": "boolean" }, { "names": "--format", "description": "Output format (text, markdown)", "defaultValue": "text", "javaType": "java.lang.String", "type": "string" }, { "names": "--model", "description": "Model to use", "defaultValue": "DEFAULT_MODEL", "javaType": "java.lang.String", "type": "string" }, { "names": "--show-prompt", "description": "Show the prompt sent to the LLM", "javaType": "boolean", "type": "boolean" }, { "names": "--stream", "description": "Stream the response as it's generated (shows progress)", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--system-prompt", "description": "Custom system prompt", "javaType": "java.lang.String", "type": "string" }, { "names": "--temperature", "description": "Temperature for response generation (0.0-2.0)", "defaultValue": "0.7", "javaType": "double", "type": "number" }, { "names": "--timeout", "description": "Timeout in seconds for LLM response", "defaultValue": "120", "javaType": "int", "type": "integer" }, { "names": "--url", "description": "LLM API endpoint URL. Auto-detected from 'camel infra' for Ollama if not specified.", "javaType": "java.lang.String", "type": "string" }, { "names": "--verbose,-v", "description": "Include detailed security recommendations with code examples", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "hawtio", "fullName": "hawtio", "description": "Launch Hawtio web console", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.Hawtio", "options": [ { "names": "--host", "description": "Hostname to bind the Hawtio web console to", "defaultValue": "127.0.0.1", "javaType": "java.lang.String", "type": "string" }, { "names": "--openUrl", "description": "To automatic open Hawtio web console in the web browser", "defaultValue": "true", "javaType": "boolean", "type": "boolean" }, { "names": "--port", "description": "Port number to use for Hawtio web console (port 8888 by default)", "defaultValue": "8888", "javaType": "int", "type": "integer" }, { "names": "--version", "description": "Version of the Hawtio web console", "defaultValue": "HawtioVersion.HAWTIO_VERSION", "javaType": "java.lang.String", "type": "string" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "infra", "fullName": "infra", "description": "List and Run external services for testing and prototyping", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.infra.InfraCommand", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "get", "fullName": "infra get", "description": "Displays running service(s) information", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.infra.InfraGet", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "list", "fullName": "infra list", "description": "Displays available external services", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.infra.InfraList", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "log", "fullName": "infra log", "description": "Displays external service logs", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.infra.InfraLog", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--lines", "description": "The number of lines from the end of the log to use as starting offset", "defaultValue": "50", "javaType": "int", "type": "integer" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "ps", "fullName": "infra ps", "description": "Displays running services", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.infra.InfraPs", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "restart", "fullName": "infra restart", "description": "Restarts a running external service", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.infra.InfraRestart", "options": [ { "names": "--background", "description": "Run in the background", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--kill", "description": "To force killing the process (SIGKILL) when stopping", "javaType": "boolean", "type": "boolean" }, { "names": "--log", "description": "Log container output to console", "javaType": "boolean", "type": "boolean" }, { "names": "--port", "description": "Override the default port for the service", "javaType": "java.lang.Integer", "type": "integer" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "run", "fullName": "infra run", "description": "Run an external service", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.infra.InfraRun", "options": [ { "names": "--background", "description": "Run in the background", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--log", "description": "Log container output to console", "javaType": "boolean", "type": "boolean" }, { "names": "--port", "description": "Override the default port for the service", "javaType": "java.lang.Integer", "type": "integer" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "stop", "fullName": "infra stop", "description": "Shuts down running external services", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.infra.InfraStop", "options": [ { "names": "--json", "description": "Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names": "--kill", "description": "To force killing the process (SIGKILL)", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] } ] }, diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java index 4d675773de1da..d8766317ac370 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java @@ -135,6 +135,10 @@ public Path getMessageHistoryFile(String pid) { return CommandLineHelper.getCamelDir().resolve(pid + "-history.json"); } + public Path getActivityFile(String pid) { + return CommandLineHelper.getCamelDir().resolve(pid + "-activity.json"); + } + public Path getRunBackgroundLogFile(String uuid) { return CommandLineHelper.getCamelDir().resolve(uuid + "-run.log"); } 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 93625b9906df6..87014e76d6f5e 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 @@ -155,6 +155,7 @@ public void execute(String... args) { .addSubcommand("ask", new CommandLine(new Ask(this))) .addSubcommand("harden", new CommandLine(new Harden(this))) .addSubcommand("get", new CommandLine(new CamelStatus(this)) + .addSubcommand("activity", new CommandLine(new ListActivity(this))) .addSubcommand("bean", new CommandLine(new CamelBeanDump(this))) .addSubcommand("blocked", new CommandLine(new ListBlocked(this))) .addSubcommand("circuit-breaker", new CommandLine(new ListCircuitBreaker(this))) diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListActivity.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListActivity.java new file mode 100644 index 0000000000000..8568d32eb1d0b --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListActivity.java @@ -0,0 +1,293 @@ +/* + * 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.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +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.dsl.jbang.core.common.ProcessHelper; +import org.apache.camel.dsl.jbang.core.common.TerminalWidthHelper; +import org.apache.camel.support.PatternHelper; +import org.apache.camel.util.TimeUtils; +import org.apache.camel.util.json.JsonArray; +import org.apache.camel.util.json.JsonObject; +import org.apache.camel.util.json.Jsoner; +import picocli.CommandLine; +import picocli.CommandLine.Command; + +@Command(name = "activity", description = "Get recent completed exchange activity", + sortOptions = false, showDefaultValues = true, + footer = { + "%nExamples:", + " camel get activity", + " camel get activity --filter=route1", + " camel get activity --watch" }) +public class ListActivity extends ProcessWatchCommand { + + public static class PidNameAgeElapsedSinceCandidates implements Iterable { + + public PidNameAgeElapsedSinceCandidates() { + } + + @Override + public Iterator iterator() { + return List.of("pid", "name", "age", "elapsed", "since").iterator(); + } + } + + @CommandLine.Parameters(description = "Name or pid of running Camel integration", arity = "0..1") + String name = "*"; + + @CommandLine.Option(names = { "--sort" }, completionCandidates = PidNameAgeElapsedSinceCandidates.class, + description = "Sort by pid, name, age, elapsed, or since", defaultValue = "since") + String sort; + + @CommandLine.Option(names = { "--limit" }, + description = "Filter activity by limiting to the given number of rows") + int limit; + + @CommandLine.Option(names = { "--filter" }, + description = "Filter activity by route ID") + String filter; + + @CommandLine.Option(names = { "--short-uri" }, + description = "List endpoint URI without query parameters (short)") + boolean shortUri; + + @CommandLine.Option(names = { "--wide-uri" }, + description = "List endpoint URI in full details") + boolean wideUri; + + public ListActivity(CamelJBangMain main) { + super(main); + } + + @Override + public Integer doProcessWatchCall() throws Exception { + List rows = new ArrayList<>(); + + if (filter != null && !filter.endsWith("*")) { + filter += "*"; + } + + List pids = findPids(name); + 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) { + String integrationName = context.getString("name"); + if ("CamelJBang".equals(integrationName)) { + integrationName = ProcessHelper.extractName(root, ph); + } + long uptime = extractSince(ph); + String age = TimeUtils.printSince(uptime); + + JsonObject activity = loadActivityFile(ph.pid()); + if (activity != null) { + JsonArray array = (JsonArray) activity.get("activity"); + if (array != null) { + for (int i = 0; i < array.size(); i++) { + JsonObject o = (JsonObject) array.get(i); + Row row = new Row(); + row.pid = Long.toString(ph.pid()); + row.name = integrationName; + row.uptime = uptime; + row.age = age; + row.exchangeId = o.getString("exchangeId"); + row.routeId = o.getString("routeId"); + row.failed = o.getBooleanOrDefault("failed", false); + row.status = row.failed ? "FAILED" : "OK"; + row.elapsed = o.getLongOrDefault("elapsed", 0); + row.timestamp = o.getLongOrDefault("timestamp", 0); + row.endpointUri = o.getString("endpointUri"); + if (row.timestamp > 0) { + long ago = System.currentTimeMillis() - row.timestamp; + row.since = TimeUtils.printSince(ago); + } else { + row.since = ""; + } + + boolean add = true; + if (filter != null) { + String f = filter; + boolean negate = filter.startsWith("-"); + if (negate) { + f = f.substring(1); + } + boolean match = PatternHelper.matchPattern(row.routeId, f); + if (negate) { + match = !match; + } + if (!match) { + add = false; + } + } + if (add) { + rows.add(row); + } + } + } + } + } + } + }); + + // sort rows + rows.sort(this::sortRow); + + if (limit > 0 && rows.size() > limit) { + rows.subList(limit, rows.size()).clear(); + } + + if (!rows.isEmpty()) { + printTable(rows); + } + + return 0; + } + + private JsonObject loadActivityFile(long pid) { + try { + Path f = getActivityFile(Long.toString(pid)); + if (f != null && Files.exists(f)) { + String text = Files.readString(f); + if (text != null && !text.isBlank()) { + return (JsonObject) Jsoner.deserialize(text); + } + } + } catch (Exception e) { + // ignore + } + return null; + } + + protected void printTable(List rows) { + if (jsonOutput) { + printer().println(Jsoner.serialize(rows.stream().map(r -> { + JsonObject jo = new JsonObject(); + jo.put("pid", r.pid); + jo.put("name", r.name); + jo.put("age", r.age); + jo.put("exchangeId", r.exchangeId); + jo.put("routeId", r.routeId); + jo.put("status", r.status); + jo.put("elapsed", r.elapsed); + jo.put("since", r.since); + if (r.endpointUri != null) { + jo.put("endpointUri", r.endpointUri); + } + return jo; + }).collect(Collectors.toList()))); + return; + } + + // Flexible column: ENDPOINT (40/140) + // Fixed columns: PID(8)+NAME(30)+AGE(8)+EXCHANGE(40)+ROUTE(25)+STATUS(6)+ELAPSED(8)+SINCE(8) ~= 133 + int fixedW = 133; + int numCols = 9; + int tw = terminalWidth(); + int uriW = TerminalWidthHelper.flexWidth(tw, fixedW, TerminalWidthHelper.noBorderOverhead(numCols), 20, 40); + int uriWideW = TerminalWidthHelper.flexWidth(tw, fixedW, TerminalWidthHelper.noBorderOverhead(numCols), 20, 140); + printer().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_RIGHT) + .with(r -> r.name), + new Column().header("AGE").headerAlign(HorizontalAlign.CENTER).with(r -> r.age), + new Column().header("EXCHANGE").dataAlign(HorizontalAlign.LEFT).maxWidth(40, OverflowBehaviour.ELLIPSIS_RIGHT) + .with(r -> r.exchangeId), + new Column().header("ROUTE").dataAlign(HorizontalAlign.LEFT).maxWidth(25, OverflowBehaviour.ELLIPSIS_RIGHT) + .with(r -> r.routeId), + new Column().header("STATUS").dataAlign(HorizontalAlign.CENTER) + .with(r -> r.status), + new Column().header("ELAPSED").headerAlign(HorizontalAlign.RIGHT).dataAlign(HorizontalAlign.RIGHT) + .with(r -> r.elapsed + "ms"), + new Column().header("SINCE").headerAlign(HorizontalAlign.CENTER) + .with(r -> r.since), + new Column().header("ENDPOINT").visible(!wideUri).dataAlign(HorizontalAlign.LEFT) + .maxWidth(uriW, OverflowBehaviour.ELLIPSIS_RIGHT) + .with(this::getUri), + new Column().header("ENDPOINT").visible(wideUri).dataAlign(HorizontalAlign.LEFT) + .maxWidth(uriWideW, OverflowBehaviour.NEWLINE) + .with(this::getUri)))); + } + + private String getUri(Row r) { + String u = r.endpointUri; + if (u == null) { + return ""; + } + if (shortUri) { + int pos = u.indexOf('?'); + if (pos > 0) { + u = u.substring(0, pos); + } + } + return u; + } + + 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; + case "elapsed": + return Long.compare(o1.elapsed, o2.elapsed) * negate; + case "since": + return Long.compare(o2.timestamp, o1.timestamp) * negate; + default: + return 0; + } + } + + static class Row { + String pid; + String name; + long uptime; + String age; + String exchangeId; + String routeId; + boolean failed; + String status; + long elapsed; + long timestamp; + String since; + String endpointUri; + } + +} diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/process/ListActivityTest.java b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/process/ListActivityTest.java new file mode 100644 index 0000000000000..f8488fbf754e2 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/process/ListActivityTest.java @@ -0,0 +1,203 @@ +/* + * 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.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; +import org.apache.camel.dsl.jbang.core.common.CommandLineHelper; +import org.apache.camel.util.json.JsonArray; +import org.apache.camel.util.json.JsonObject; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedStatic; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mockStatic; + +@ExtendWith(MockitoExtension.class) +class ListActivityTest extends ProcessCommandTestSupport { + + @Test + void testEmptyOutputWhenNoActivityFile() throws Exception { + writeStatusFile(TEST_PID, buildContextStatus("myApp", 5)); + + ListActivity command = createCommand(); + + try (MockedStatic mocked = mockStatic(ProcessHandle.class)) { + ProcessHandle ph = mockProcessHandle(TEST_PID); + ProcessHandle currentHandle = mockCurrentHandle(); + mocked.when(ProcessHandle::current).thenReturn(currentHandle); + mocked.when(ProcessHandle::allProcesses).thenAnswer(inv -> Stream.of(ph)); + + int exit = command.doCall(); + + assertEquals(0, exit); + assertEquals("", printer.getOutput().trim()); + } + } + + @Test + void testShowsActivityEntries() throws Exception { + writeStatusFile(TEST_PID, buildContextStatus("myApp", 5)); + writeActivityFile(TEST_PID, activityJson( + activityEntry("EX-001", "route1", false, 42, "direct://start"), + activityEntry("EX-002", "route2", true, 150, "timer://tick"))); + + ListActivity command = createCommand(); + + try (MockedStatic mocked = mockStatic(ProcessHandle.class)) { + ProcessHandle ph = mockProcessHandle(TEST_PID); + ProcessHandle currentHandle = mockCurrentHandle(); + mocked.when(ProcessHandle::current).thenReturn(currentHandle); + mocked.when(ProcessHandle::allProcesses).thenAnswer(inv -> Stream.of(ph)); + + int exit = command.doCall(); + + assertEquals(0, exit); + String output = printer.getOutput(); + assertTrue(output.contains("EX-001"), "Should show first exchange id"); + assertTrue(output.contains("EX-002"), "Should show second exchange id"); + assertTrue(output.contains("route1"), "Should show first route id"); + assertTrue(output.contains("route2"), "Should show second route id"); + assertTrue(output.contains("OK"), "Should show OK status"); + assertTrue(output.contains("FAILED"), "Should show FAILED status"); + assertTrue(output.contains("42ms"), "Should show elapsed time"); + assertTrue(output.contains("150ms"), "Should show elapsed time"); + } + } + + @Test + void testFilterByRoute() throws Exception { + writeStatusFile(TEST_PID, buildContextStatus("myApp", 5)); + writeActivityFile(TEST_PID, activityJson( + activityEntry("EX-001", "route1", false, 42, "direct://start"), + activityEntry("EX-002", "route2", false, 100, "timer://tick"))); + + ListActivity command = createCommand(); + command.filter = "route1"; + + try (MockedStatic mocked = mockStatic(ProcessHandle.class)) { + ProcessHandle ph = mockProcessHandle(TEST_PID); + ProcessHandle currentHandle = mockCurrentHandle(); + mocked.when(ProcessHandle::current).thenReturn(currentHandle); + mocked.when(ProcessHandle::allProcesses).thenAnswer(inv -> Stream.of(ph)); + + int exit = command.doCall(); + + assertEquals(0, exit); + String output = printer.getOutput(); + assertTrue(output.contains("route1"), "Should show matching route"); + assertFalse(output.contains("route2"), "Should not show filtered-out route"); + } + } + + @Test + void testJsonOutput() throws Exception { + writeStatusFile(TEST_PID, buildContextStatus("myApp", 5)); + writeActivityFile(TEST_PID, activityJson( + activityEntry("EX-100", "myRoute", false, 55, "direct://foo"))); + + ListActivity command = createCommand(); + command.jsonOutput = true; + + try (MockedStatic mocked = mockStatic(ProcessHandle.class)) { + ProcessHandle ph = mockProcessHandle(TEST_PID); + ProcessHandle currentHandle = mockCurrentHandle(); + mocked.when(ProcessHandle::current).thenReturn(currentHandle); + mocked.when(ProcessHandle::allProcesses).thenAnswer(inv -> Stream.of(ph)); + + int exit = command.doCall(); + + assertEquals(0, exit); + String output = printer.getOutput(); + assertTrue(output.startsWith("["), "JSON output should be array"); + assertTrue(output.contains("EX-100")); + assertTrue(output.contains("myRoute")); + assertTrue(output.contains("\"status\":\"OK\"")); + } + } + + @Test + void testLimitRows() throws Exception { + writeStatusFile(TEST_PID, buildContextStatus("myApp", 5)); + writeActivityFile(TEST_PID, activityJson( + activityEntry("EX-001", "route1", false, 10, "direct://a"), + activityEntry("EX-002", "route1", false, 20, "direct://a"), + activityEntry("EX-003", "route1", false, 30, "direct://a"))); + + ListActivity command = createCommand(); + command.limit = 2; + + try (MockedStatic mocked = mockStatic(ProcessHandle.class)) { + ProcessHandle ph = mockProcessHandle(TEST_PID); + ProcessHandle currentHandle = mockCurrentHandle(); + mocked.when(ProcessHandle::current).thenReturn(currentHandle); + mocked.when(ProcessHandle::allProcesses).thenAnswer(inv -> Stream.of(ph)); + + int exit = command.doCall(); + + assertEquals(0, exit); + String output = printer.getOutput(); + // count exchange ID occurrences — should be limited to 2 + long count = output.lines().filter(l -> l.contains("EX-")).count(); + assertEquals(2, count, "Should limit to 2 rows"); + } + } + + private ListActivity createCommand() { + ListActivity cmd = new ListActivity(new CamelJBangMain().withPrinter(printer)); + cmd.sort = "since"; + return cmd; + } + + private static void writeActivityFile(long pid, JsonObject activity) throws Exception { + Path f = CommandLineHelper.getCamelDir().resolve(pid + "-activity.json"); + Files.writeString(f, activity.toJson()); + } + + private static JsonObject activityJson(JsonObject... entries) { + JsonArray arr = new JsonArray(); + for (JsonObject entry : entries) { + arr.add(entry); + } + JsonObject root = new JsonObject(); + root.put("activitySize", 100); + root.put("activity", arr); + return root; + } + + private static JsonObject activityEntry( + String exchangeId, String routeId, boolean failed, long elapsed, String endpointUri) { + JsonObject e = new JsonObject(); + e.put("uid", System.nanoTime()); + e.put("exchangeId", exchangeId); + e.put("routeId", routeId); + e.put("failed", failed); + e.put("elapsed", elapsed); + e.put("timestamp", System.currentTimeMillis()); + if (endpointUri != null) { + e.put("endpointUri", endpointUri); + } + return e; + } +} diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActivityEntry.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActivityEntry.java new file mode 100644 index 0000000000000..a2046b5c095b6 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActivityEntry.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.dsl.jbang.core.commands.tui; + +import java.util.ArrayList; +import java.util.List; + +class ActivityEntry { + long uid; + String exchangeId; + String routeId; + String fromRouteId; + String fromEndpointUri; + long timestamp; + long elapsed; + boolean failed; + String exceptionMessage; + final List endpointSends = new ArrayList<>(); + + static class EndpointSendEntry { + String endpointUri; + boolean remoteEndpoint; + long elapsed; + } +} diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActivityTab.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActivityTab.java new file mode 100644 index 0000000000000..7bcf4087f672c --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ActivityTab.java @@ -0,0 +1,462 @@ +/* + * 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.tui; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import dev.tamboui.layout.Constraint; +import dev.tamboui.layout.Layout; +import dev.tamboui.layout.Rect; +import dev.tamboui.style.Style; +import dev.tamboui.terminal.Frame; +import dev.tamboui.text.Line; +import dev.tamboui.text.Span; +import dev.tamboui.text.Text; +import dev.tamboui.tui.event.KeyEvent; +import dev.tamboui.widgets.block.Block; +import dev.tamboui.widgets.block.BorderType; +import dev.tamboui.widgets.block.Borders; +import dev.tamboui.widgets.paragraph.Paragraph; +import dev.tamboui.widgets.scrollbar.ScrollbarState; +import dev.tamboui.widgets.table.Cell; +import dev.tamboui.widgets.table.Row; +import dev.tamboui.widgets.table.Table; +import org.apache.camel.util.TimeUtils; +import org.apache.camel.util.json.JsonObject; + +class ActivityTab extends AbstractTableTab { + + private final ScrollbarState detailScrollState = new ScrollbarState(); + private int detailScroll; + private int detailHScroll; + private boolean wordWrap = true; + private boolean paused; + private List pausedSnapshot; + private int timeFilterIndex; + private static final String[] TIME_FILTERS = { "all", "1m", "5m", "15m", "30m", "1h" }; + private static final long[] TIME_FILTER_MILLIS = { 0, 60_000, 300_000, 900_000, 1_800_000, 3_600_000 }; + + ActivityTab(MonitorContext ctx) { + super(ctx, "exchange", "route", "elapsed", "since"); + } + + @Override + protected int getRowCount() { + IntegrationInfo info = ctx.findSelectedIntegration(); + if (info == null) { + return 0; + } + return filteredActivity(info).size(); + } + + @Override + public void onTabSelected() { + IntegrationInfo info = ctx.findSelectedIntegration(); + if (info != null && !info.activity.isEmpty() && tableState.selected() == null) { + tableState.select(0); + } + } + + @Override + protected boolean handleTabKeyEvent(KeyEvent ke) { + if (ke.isConfirm()) { + return true; + } + if (ke.isChar('w')) { + wordWrap = !wordWrap; + return true; + } + if (ke.isChar(' ')) { + paused = !paused; + if (paused) { + IntegrationInfo info = ctx.findSelectedIntegration(); + pausedSnapshot = info != null ? new ArrayList<>(info.activity) : List.of(); + } else { + pausedSnapshot = null; + } + return true; + } + if (ke.isChar('t')) { + timeFilterIndex = (timeFilterIndex + 1) % TIME_FILTERS.length; + return true; + } + if (ke.isPageUp()) { + detailScroll = Math.max(0, detailScroll - 10); + return true; + } + if (ke.isPageDown()) { + detailScroll += 10; + return true; + } + if (ke.isLeft()) { + detailHScroll = Math.max(0, detailHScroll - 5); + return true; + } + if (ke.isRight()) { + detailHScroll += 5; + return true; + } + return false; + } + + @Override + public void navigateUp() { + detailScroll = 0; + super.navigateUp(); + } + + @Override + public void navigateDown() { + detailScroll = 0; + super.navigateDown(); + } + + @Override + protected void renderContent(Frame frame, Rect area, IntegrationInfo info) { + List sorted = filteredActivity(info); + + List rows = new ArrayList<>(); + for (ActivityEntry ae : sorted) { + String ago = ae.timestamp > 0 + ? TimeUtils.printSince(ae.timestamp) : ""; + String status = ae.failed ? "FAILED" : "OK"; + Style statusStyle = ae.failed ? Theme.error() : Theme.success(); + String elapsed = ae.elapsed + "ms"; + + rows.add(Row + .from( + Cell.from(ae.exchangeId != null ? ae.exchangeId : ""), + Cell.from(Span.styled(ae.routeId != null ? ae.routeId : "", Style.EMPTY.fg(Theme.accent()))), + Cell.from(Span.styled(status, statusStyle)), + Cell.from(elapsed), + Cell.from(ago), + Cell.from(ae.fromEndpointUri != null ? ae.fromEndpointUri : ""))); + } + + if (rows.isEmpty()) { + rows.add(emptyRow("No activity captured", 6)); + } + + ActivityEntry selectedEntry = null; + Integer sel = tableState.selected(); + if (sel != null && sel >= 0 && sel < sorted.size()) { + selectedEntry = sorted.get(sel); + } + boolean showDetail = selectedEntry != null; + + List constraints = new ArrayList<>(); + constraints.add(Constraint.length(4)); + if (showDetail) { + constraints.add(Constraint.length(13)); + constraints.add(Constraint.fill()); + } else { + constraints.add(Constraint.fill()); + } + List chunks = Layout.vertical() + .constraints(constraints) + .split(area); + + renderStatsPanel(frame, chunks.get(0), sorted); + + Table table = Table.builder() + .rows(rows) + .header(Row.from( + Cell.from(Span.styled(sortLabel("EXCHANGE", "exchange"), sortStyle("exchange"))), + Cell.from(Span.styled(sortLabel("ROUTE", "route"), sortStyle("route"))), + Cell.from(Span.styled("STATUS", Style.EMPTY.bold())), + Cell.from(Span.styled(sortLabel("ELAPSED", "elapsed"), sortStyle("elapsed"))), + Cell.from(Span.styled(sortLabel("SINCE", "since"), sortStyle("since"))), + Cell.from(Span.styled("ENDPOINT", Style.EMPTY.bold())))) + .widths( + Constraint.length(38), + Constraint.length(20), + Constraint.length(8), + Constraint.length(10), + Constraint.length(8), + Constraint.fill()) + .highlightStyle(Theme.selectionBg()) + .highlightSpacing(Table.HighlightSpacing.ALWAYS) + .block(Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL) + .title(" Activity [" + sorted.size() + "] ").build()) + .build(); + + lastTableArea = chunks.get(1); + frame.renderStatefulWidget(table, chunks.get(1), tableState); + renderScrollbar(frame, sorted.size()); + + if (showDetail) { + renderDetail(frame, chunks.get(2), selectedEntry); + } + } + + private void renderStatsPanel(Frame frame, Rect area, List entries) { + int total = entries.size(); + int failed = 0; + long maxElapsed = 0; + long oldestTs = Long.MAX_VALUE; + long newestTs = 0; + long[] elapsedValues = new long[total]; + + for (int i = 0; i < total; i++) { + ActivityEntry ae = entries.get(i); + elapsedValues[i] = ae.elapsed; + if (ae.failed) { + failed++; + } + if (ae.elapsed > maxElapsed) { + maxElapsed = ae.elapsed; + } + if (ae.timestamp > 0 && ae.timestamp < oldestTs) { + oldestTs = ae.timestamp; + } + if (ae.timestamp > newestTs) { + newestTs = ae.timestamp; + } + } + + java.util.Arrays.sort(elapsedValues); + long p50 = total > 0 ? elapsedValues[Math.min((int) (total * 0.50), total - 1)] : 0; + long p95 = total > 0 ? elapsedValues[Math.min((int) (total * 0.95), total - 1)] : 0; + + String errorRate = total > 0 + ? String.format(Locale.US, "%.1f%%", (failed * 100.0) / total) : "0%"; + String rate = ""; + if (oldestTs < Long.MAX_VALUE && newestTs > oldestTs) { + double minutes = (newestTs - oldestTs) / 60_000.0; + if (minutes > 0) { + rate = String.format(Locale.US, "%.1f/min", total / minutes); + } + } + + int sends = entries.stream().mapToInt(ae -> ae.endpointSends.size()).sum(); + + Style dim = Theme.muted(); + List lines = new ArrayList<>(); + + lines.add(Line.from( + Span.styled(" Total: ", dim), Span.raw(String.valueOf(total)), + Span.styled(" OK: ", dim), Span.styled(String.valueOf(total - failed), Theme.success()), + Span.styled(" Failed: ", dim), + Span.styled(failed + " (" + errorRate + ")", failed > 0 ? Theme.error() : Style.EMPTY), + Span.styled(" Rate: ", dim), Span.raw(rate), + Span.styled(" Sends: ", dim), Span.raw(String.valueOf(sends)))); + + lines.add(Line.from( + Span.styled(" p50: ", dim), Span.raw(p50 + "ms"), + Span.styled(" p95: ", dim), + Span.styled(p95 + "ms", TuiHelper.topTimeStyle(p95)), + Span.styled(" Max: ", dim), + Span.styled(maxElapsed + "ms", TuiHelper.topTimeStyle(maxElapsed)), + oldestTs < Long.MAX_VALUE + ? Span.styled(" Window: ", dim) + : Span.raw(""), + oldestTs < Long.MAX_VALUE + ? Span.raw(TimeUtils.printSince(oldestTs) + + " ... " + TimeUtils.printSince(newestTs)) + : Span.raw(""))); + + String title = paused + ? " Summary (PAUSED, last " + total + ") " + : " Summary (last " + total + ") "; + Block block = Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL) + .title(title).build(); + Paragraph para = Paragraph.builder().text(Text.from(lines)).block(block).build(); + frame.renderWidget(para, area); + } + + @Override + public void renderFooter(List spans) { + TuiHelper.hint(spans, "Esc", "back"); + TuiHelper.hint(spans, TuiIcons.HINT_SCROLL, "navigate"); + TuiHelper.hint(spans, "PgUp/Dn", "detail"); + if (!wordWrap) { + TuiHelper.hint(spans, TuiIcons.HINT_H, "h-scroll"); + } + TuiHelper.hint(spans, "Home/End", "top/end"); + TuiHelper.hint(spans, "s", "sort"); + TuiHelper.hint(spans, "Space", paused ? "resume" : "pause"); + TuiHelper.hint(spans, "t", TIME_FILTERS[timeFilterIndex]); + TuiHelper.hintLast(spans, "w", "wrap" + (wordWrap ? " [on]" : " [off]")); + } + + private void renderDetail(Frame frame, Rect area, ActivityEntry ae) { + List lines = new ArrayList<>(); + + HistoryTab.addExchangeInfoLines(lines, + ae.exchangeId, ae.routeId, null, null, ae.fromEndpointUri, + ae.elapsed, null, ae.failed); + + if (ae.exceptionMessage != null) { + HistoryTab.addExceptionLines(lines, ae.exceptionMessage); + } + + if (!ae.endpointSends.isEmpty()) { + lines.add(Line.from(Span.styled(" Endpoint Sends:", Style.EMPTY.fg(Theme.accent()).bold()))); + for (ActivityEntry.EndpointSendEntry se : ae.endpointSends) { + lines.add(Line + .from( + Span.styled(" " + (se.endpointUri != null ? se.endpointUri : ""), Style.EMPTY), + Span.styled(" " + se.elapsed + "ms", Theme.muted()))); + } + } + + int[] scroll = { detailScroll }; + int[] hScroll = { detailHScroll }; + HistoryTab.renderDetailPanel(frame, area, lines, wordWrap, hScroll, scroll, detailScrollState, " Detail "); + detailScroll = scroll[0]; + detailHScroll = hScroll[0]; + } + + private List filteredActivity(IntegrationInfo info) { + List source = paused && pausedSnapshot != null ? pausedSnapshot : info.activity; + List result = new ArrayList<>(source); + long filterMillis = TIME_FILTER_MILLIS[timeFilterIndex]; + if (filterMillis > 0) { + long cutoff = System.currentTimeMillis() - filterMillis; + result.removeIf(ae -> ae.timestamp > 0 && ae.timestamp < cutoff); + } + result.sort(this::sortActivity); + return result; + } + + private int sortActivity(ActivityEntry a, ActivityEntry b) { + int result = switch (sort) { + case "exchange" -> TuiHelper.compareStr(a.exchangeId, b.exchangeId); + case "route" -> TuiHelper.compareStr(a.routeId, b.routeId); + case "elapsed" -> Long.compare(a.elapsed, b.elapsed); + case "since" -> Long.compare(b.timestamp, a.timestamp); + default -> 0; + }; + return sortReversed ? -result : result; + } + + @Override + public SelectionContext getSelectionContext() { + IntegrationInfo info = ctx.findSelectedIntegration(); + if (info == null) { + return null; + } + List sorted = filteredActivity(info); + Integer sel = tableState.selected(); + if (sel != null && sel >= 0 && sel < sorted.size()) { + List items = sorted.stream() + .map(ae -> ae.exchangeId != null ? ae.exchangeId : "") + .toList(); + return new SelectionContext("activity-entry", items, sel, sorted.size(), "Activity"); + } + return null; + } + + @Override + public JsonObject getTableDataAsJson() { + IntegrationInfo info = ctx.findSelectedIntegration(); + JsonObject result = new JsonObject(); + result.put("tab", "Activity"); + if (info == null) { + result.put("rows", new ArrayList<>()); + result.put("totalRows", 0); + return result; + } + List sorted = filteredActivity(info); + List jsonRows = new ArrayList<>(); + for (ActivityEntry ae : sorted) { + JsonObject jo = new JsonObject(); + jo.put("exchangeId", ae.exchangeId); + jo.put("routeId", ae.routeId); + jo.put("status", ae.failed ? "FAILED" : "OK"); + jo.put("elapsed", ae.elapsed); + jo.put("failed", ae.failed); + jo.put("timestamp", ae.timestamp); + if (ae.timestamp > 0) { + jo.put("since", TimeUtils.printSince(ae.timestamp)); + } + if (ae.fromEndpointUri != null) { + jo.put("fromEndpointUri", ae.fromEndpointUri); + } + jsonRows.add(jo); + } + result.put("rows", jsonRows); + result.put("totalRows", sorted.size()); + Integer sel = tableState.selected(); + result.put("selectedIndex", sel != null ? sel : -1); + return result; + } + + @Override + public String description() { + return "Recent completed exchange activity with message details"; + } + + @Override + public String getHelpText() { + return """ + # Activity + + The Activity tab shows a live feed of recently completed exchanges. + It captures the last N exchanges (default 100) with their message + content, giving you a rolling window of what your integration has + been processing. + + Unlike the Inspect tab which traces individual processing steps + within a route, Activity shows one entry per completed exchange + with a summary of the final state. + + ## Summary Panel + + The top panel shows aggregated stats from the visible activity: + + - **Total** / **OK** / **Failed** — exchange counts + - **Sends** — total remote endpoint calls across all exchanges + - **Avg** / **Max** — elapsed time statistics + - **Window** — how far back the oldest and newest entries are + + ## Activity List + + - **EXCHANGE** — Exchange identifier + - **ROUTE** — Route that processed the exchange + - **STATUS** — `OK` (green) or `FAILED` (red) + - **ELAPSED** — Total processing time in milliseconds + - **SINCE** — How long ago the exchange completed (e.g., `5s`, `2m`) + - **ENDPOINT** — The consumer endpoint that received the exchange + + ## Detail View + + Select an exchange to see its details in the panel below: + + - **Exchange info**: Exchange ID, route, elapsed time, and failure status + - **Endpoint Sends**: Remote endpoints called during the exchange, + with individual elapsed times + - **Exception**: If the exchange failed, shows the exception type, + message, and stack trace + + ## Keys + + - `Up/Down` — select exchange + - `Home/End` — jump to first/last exchange + - `PgUp/PgDn` — scroll the detail panel + - `Left/Right` — horizontal scroll (when wrap is off) + - `Space` — pause/resume data feed (freezes the view for inspection) + - `t` — cycle time filter (all, 1m, 5m, 15m, 30m, 1h) + - `w` — toggle word wrap + - `s` — cycle sort column + - `S` — reverse sort order + """; + } + +} diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AiLogPopup.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AiLogPopup.java index f2fb1e532ca9a..90cb734186d0d 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AiLogPopup.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AiLogPopup.java @@ -123,7 +123,7 @@ void render(Frame frame, Rect area) { void renderFooter(List spans) { hint(spans, TuiIcons.HINT_SCROLL, "select"); - hint(spans, "PgUp/Dn", "scroll detail"); + hint(spans, "PgUp/Dn", "detail"); hintLast(spans, "Esc", "back"); } diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java index 89fdcde8f723b..6f1882c33d208 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java @@ -299,6 +299,11 @@ public void refreshErrorData(List pids) { dataService.refreshErrorData(pids); } + @Override + public void refreshActivityData(List pids) { + dataService.refreshActivityData(pids); + } + @Override public void openMorePopup() { popupManager.openMorePopup(); @@ -1466,6 +1471,7 @@ private void renderTabs(Frame frame, Rect area) { Line.from(TuiIcons.primaryTabHeader(TuiIcons.TAB_HEALTH, "7", "Health")), Line.from(TuiIcons.primaryTabHeader(TuiIcons.TAB_INSPECT, "8", "Inspect")), Line.from(TuiIcons.primaryTabHeader(TuiIcons.TAB_ERRORS, "9", "Errors")), + Line.from(TuiIcons.primaryTabHeader(TuiIcons.TAB_ACTIVITY, "", "Activity")), Line.from(TuiIcons.primaryTabHeader(TuiIcons.TAB_MORE, "0", TuiIcons.moreTabLabel())), }; popupManager.setCurrentTabLabels(labels); @@ -1627,6 +1633,10 @@ private void computeTabBadges(String[] badgeTexts, Style[] badgeStyles) { badgeTexts[TAB_ERRORS] = "(" + errorCount + ")"; badgeStyles[TAB_ERRORS] = red; } + int activityCount = hasSelection ? sel.activity.size() : 0; + if (activityCount > 0) { + badgeTexts[TAB_ACTIVITY] = "(" + activityCount + ")"; + } } private void openFilesPopup() { @@ -2103,6 +2113,9 @@ private void refreshConditionalData() { if (tabRegistry.selectedTabIndex() == TAB_ERRORS && !selectedPids.isEmpty()) { dataService.refreshErrorData(selectedPids); } + if (tabRegistry.selectedTabIndex() == TAB_ACTIVITY && !selectedPids.isEmpty()) { + dataService.refreshActivityData(selectedPids); + } if (tabRegistry.selectedTabIndex() == TAB_HISTORY && !selectedPids.isEmpty()) { if (tabRegistry.historyTab().historyRefreshRequested) { tabRegistry.historyTab().historyRefreshRequested = false; diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DataRefreshService.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DataRefreshService.java index cc328decbc061..5c30e084330e9 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DataRefreshService.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/DataRefreshService.java @@ -657,6 +657,32 @@ List loadHistoryData(List pids) { return allEntries; } + void refreshActivityData(List pids) { + for (Long pid : pids) { + IntegrationInfo sel = ctx.data.get().stream() + .filter(i -> String.valueOf(pid).equals(i.pid) && !i.vanishing) + .findFirst().orElse(null); + if (sel == null) { + continue; + } + try { + Path activityFile = CommandLineHelper.getCamelDir().resolve(pid + "-activity.json"); + if (!Files.exists(activityFile)) { + continue; + } + String content = Files.readString(activityFile); + if (content != null && !content.isBlank()) { + JsonObject json = (JsonObject) Jsoner.deserialize(content); + List parsed = StatusParser.parseActivityEntries(json); + sel.activity.clear(); + sel.activity.addAll(parsed); + } + } catch (Exception e) { + // ignore + } + } + } + // ---- Helpers ---- private void detectReload(IntegrationInfo info) { diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorsTab.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorsTab.java index d64f8ce25b816..e24414dbca4a5 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorsTab.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorsTab.java @@ -359,7 +359,7 @@ protected void renderContent(Frame frame, Rect area, IntegrationInfo info) { boolean showDetail = selectedError != null; List chunks = showDetail ? Layout.vertical() - .constraints(Constraint.length(13), Constraint.length(1), Constraint.fill()) + .constraints(Constraint.length(13), Constraint.fill()) .split(area) : List.of(area); @@ -392,7 +392,7 @@ protected void renderContent(Frame frame, Rect area, IntegrationInfo info) { renderScrollbar(frame, filteredSize()); if (showDetail) { - renderDetail(frame, chunks.get(2), selectedError); + renderDetail(frame, chunks.get(1), selectedError); } } @@ -431,7 +431,7 @@ public void renderFooter(List spans) { } hint(spans, "Esc", "back"); hint(spans, TuiIcons.HINT_SCROLL, "navigate"); - hint(spans, "PgUp/Dn", "scroll detail"); + hint(spans, "PgUp/Dn", "detail"); if (!wordWrap) { hint(spans, TuiIcons.HINT_H, "h-scroll"); } @@ -453,17 +453,6 @@ private void renderDetail(Frame frame, Rect area, ErrorInfo ei) { // exception with stack trace String exception = null; if (ei.exceptionType != null) { - StringBuilder sb = new StringBuilder(); - sb.append(ei.exceptionType); - if (ei.exceptionMessage != null) { - String msg = ei.exceptionMessage; - try { - msg = Jsoner.unescape(msg); - } catch (Exception e) { - // ignore - } - sb.append(": ").append(msg); - } if (ei.stackTrace != null) { String st = ei.stackTrace; try { @@ -471,17 +460,64 @@ private void renderDetail(Frame frame, Rect area, ErrorInfo ei) { } catch (Exception e) { // ignore } - sb.append("\n").append(st); + if (st.startsWith(ei.exceptionType)) { + // stackTrace already contains the exception type and message + exception = st; + } else { + StringBuilder sb = new StringBuilder(); + sb.append(ei.exceptionType); + if (ei.exceptionMessage != null) { + String msg = ei.exceptionMessage; + try { + msg = Jsoner.unescape(msg); + } catch (Exception e) { + // ignore + } + sb.append(": ").append(msg); + } + sb.append("\n").append(st); + exception = sb.toString(); + } + } else { + StringBuilder sb = new StringBuilder(); + sb.append(ei.exceptionType); + if (ei.exceptionMessage != null) { + String msg = ei.exceptionMessage; + try { + msg = Jsoner.unescape(msg); + } catch (Exception e) { + // ignore + } + sb.append(": ").append(msg); + } + exception = sb.toString(); } - exception = sb.toString(); } HistoryTab.addExceptionLines(lines, exception); // message history if (ei.messageHistory != null && ei.messageHistory.length > 0) { lines.add(Line.from(Span.styled(" Message History:", Theme.notice().bold()))); - for (String step : ei.messageHistory) { - lines.add(Line.from(Span.raw(" " + TuiHelper.fixControlChars(step)))); + for (int i = 0; i < ei.messageHistory.length; i++) { + String s = TuiHelper.fixControlChars(ei.messageHistory[i]); + boolean lastAndFailed = !ei.handled && i == ei.messageHistory.length - 1; + int bracket = s.indexOf('['); + if (bracket > 0) { + String routeId = s.substring(0, bracket); + String rest = s.substring(bracket); + if (lastAndFailed) { + lines.add(Line.from( + Span.styled(" " + routeId, Theme.error()), + Span.styled(rest, Theme.error()))); + } else { + lines.add(Line.from( + Span.styled(" " + routeId, Style.EMPTY.fg(Theme.accent())), + Span.styled(rest, Theme.muted()))); + } + } else { + lines.add(Line.from(Span.styled(" " + s, + lastAndFailed ? Theme.error() : Style.EMPTY))); + } } lines.add(Line.from(Span.raw(""))); } @@ -502,7 +538,7 @@ private void renderDetail(Frame frame, Rect area, ErrorInfo ei) { int[] scroll = { detailScroll }; int[] hScroll = { detailHScroll }; - HistoryTab.renderDetailPanel(frame, area, lines, wordWrap, hScroll, scroll, detailScrollState); + HistoryTab.renderDetailPanel(frame, area, lines, wordWrap, hScroll, scroll, detailScrollState, " Detail "); detailScroll = scroll[0]; detailHScroll = hScroll[0]; } @@ -684,7 +720,7 @@ private static void addKvLines(List lines, Map map) { } private static void hintShowBhpv(List spans, boolean body, boolean headers, boolean props, boolean vars) { - spans.add(Span.styled(" show", Theme.hintKey())); + spans.add(Span.styled(" show ", Theme.hintKey())); spans.add(Span.raw(" ")); spans.add(Span.styled(body ? "B" : "b", body ? Style.EMPTY.fg(Theme.baseFg()).bold() : Style.EMPTY.dim())); spans.add(Span.styled(headers ? "H" : "h", headers ? Style.EMPTY.fg(Theme.baseFg()).bold() : Style.EMPTY.dim())); diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java index 831dc3b69b1d1..f4469bc35343f 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java @@ -1327,7 +1327,7 @@ private void renderTraceStepDetail(Frame frame, Rect area, List step int[] scroll = { traceDetailScroll }; int[] hScroll = { traceDetailHScroll }; - renderDetailPanel(frame, area, lines, traceWordWrap, hScroll, scroll, traceDetailScrollState); + renderDetailPanel(frame, area, lines, traceWordWrap, hScroll, scroll, traceDetailScrollState, " Detail "); traceDetailScroll = scroll[0]; traceDetailHScroll = hScroll[0]; } @@ -1606,7 +1606,7 @@ private void renderHistoryDetail(Frame frame, Rect area, List curr int[] scroll = { historyDetailScroll }; int[] hScroll = { historyDetailHScroll }; - renderDetailPanel(frame, area, lines, historyWordWrap, hScroll, scroll, historyDetailScrollState); + renderDetailPanel(frame, area, lines, historyWordWrap, hScroll, scroll, historyDetailScrollState, " Detail "); historyDetailScroll = scroll[0]; historyDetailHScroll = hScroll[0]; } @@ -1880,7 +1880,7 @@ private Set computeAllChildExchangeIds(List exchangeIds) { } private static void hintShowBhpv(List spans, boolean body, boolean headers, boolean props, boolean vars) { - spans.add(Span.styled(" show", Theme.hintKey())); + spans.add(Span.styled(" show ", Theme.hintKey())); spans.add(Span.raw(" ")); spans.add(Span.styled(body ? "B" : "b", body ? Style.EMPTY.fg(Theme.baseFg()).bold() : Style.EMPTY.dim())); spans.add(Span.styled(headers ? "H" : "h", headers ? Style.EMPTY.fg(Theme.baseFg()).bold() : Style.EMPTY.dim())); @@ -2162,7 +2162,17 @@ static void addExceptionLines(List lines, String exception) { static void renderDetailPanel( Frame frame, Rect area, List lines, boolean wordWrap, int[] hScroll, int[] scroll, ScrollbarState scrollState) { - Block block = Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL).build(); + renderDetailPanel(frame, area, lines, wordWrap, hScroll, scroll, scrollState, null); + } + + static void renderDetailPanel( + Frame frame, Rect area, List lines, + boolean wordWrap, int[] hScroll, int[] scroll, ScrollbarState scrollState, String title) { + Block.Builder bb = Block.builder().borderType(BorderType.ROUNDED).borders(Borders.ALL); + if (title != null) { + bb.title(title); + } + Block block = bb.build(); frame.renderWidget(block, area); Rect inner = block.inner(area); diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InfraBrowserPopup.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InfraBrowserPopup.java index d638f9ec5ee24..9f9d14405fbdd 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InfraBrowserPopup.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/InfraBrowserPopup.java @@ -70,6 +70,7 @@ record InfraServiceEntry(String alias, String description, List implemen private List catalog; private InfraServiceEntry selectedService; private int implIndex; + private int portDialogRow; // 0 = impl, 1 = port private TextInputState portState; private final Supplier> infraServices; @@ -193,6 +194,13 @@ void render(Frame frame, Rect area) { void renderFooter(List spans) { if (showPortDialog) { + boolean hasMultiImpl = selectedService != null && selectedService.implementations().size() > 1; + if (hasMultiImpl) { + TuiHelper.hint(spans, TuiIcons.HINT_SCROLL, "navigate"); + if (portDialogRow == 0) { + TuiHelper.hint(spans, "Space", "cycle"); + } + } TuiHelper.hint(spans, "Enter", "run"); TuiHelper.hintLast(spans, "Esc", "back"); } else if (showBrowser) { @@ -255,8 +263,21 @@ private boolean handlePortDialogKey(KeyEvent ke) { launchService(); return true; } - if (selectedService != null && selectedService.implementations().size() > 1) { - if (ke.isLeft()) { + boolean hasMultiImpl = selectedService != null && selectedService.implementations().size() > 1; + if (ke.isUp() || ke.isFocusPrevious()) { + if (hasMultiImpl && portDialogRow > 0) { + portDialogRow--; + } + return true; + } + if (ke.isDown() || ke.isFocusNext()) { + if (hasMultiImpl && portDialogRow < 1) { + portDialogRow++; + } + return true; + } + if (portDialogRow == 0 && hasMultiImpl) { + if (ke.isLeft() || ke.isChar(' ')) { implIndex = (implIndex - 1 + selectedService.implementations().size()) % selectedService.implementations().size(); return true; @@ -265,6 +286,7 @@ private boolean handlePortDialogKey(KeyEvent ke) { implIndex = (implIndex + 1) % selectedService.implementations().size(); return true; } + return true; } handlePortInput(ke); return true; @@ -300,6 +322,7 @@ private void selectService() { } selectedService = entry; implIndex = 0; + portDialogRow = entry.implementations().size() > 1 ? 0 : 1; portState = new TextInputState(""); showBrowser = false; showPortDialog = true; @@ -410,26 +433,45 @@ private void renderPortDialog(Frame frame, Rect area) { if (hasMultiImpl) { row++; + Style implLabelStyle = portDialogRow == 0 ? Style.EMPTY.bold() : Style.EMPTY.dim(); Rect labelArea = new Rect(ix, row, labelW, 1); - frame.renderWidget(Paragraph.from(Line.from(Span.styled("Impl:", Theme.muted()))), labelArea); - String impl = selectedService.implementations().get(implIndex); + frame.renderWidget(Paragraph.from(Line.from(Span.styled("Impl:", implLabelStyle))), labelArea); Rect implArea = new Rect(ix + labelW, row, fieldW, 1); - frame.renderWidget(Paragraph.from(Line.from( - Span.styled("◀ ", Theme.hintKey()), - Span.raw(impl), - Span.styled(" ▶", Theme.hintKey()))), implArea); + List implSpans = new ArrayList<>(); + for (int i = 0; i < selectedService.implementations().size(); i++) { + if (i > 0) { + implSpans.add(Span.styled(" ", Style.EMPTY)); + } + String label = selectedService.implementations().get(i); + if (i == implIndex) { + implSpans.add(Span.styled("[" + label + "]", + portDialogRow == 0 ? Style.EMPTY.bold() : Style.EMPTY)); + } else { + implSpans.add(Span.styled(" " + label + " ", Style.EMPTY.dim())); + } + } + frame.renderWidget(Paragraph.from(Line.from(implSpans)), implArea); row++; } row++; - Rect labelArea = new Rect(ix, row, labelW, 1); - frame.renderWidget(Paragraph.from(Line.from(Span.styled("Port:", Theme.muted()))), labelArea); + Style portLabelStyle = portDialogRow == 1 ? Style.EMPTY.bold() : Style.EMPTY.dim(); + Rect portLabelArea = new Rect(ix, row, labelW, 1); + frame.renderWidget(Paragraph.from(Line.from(Span.styled("Port:", portLabelStyle))), portLabelArea); Rect portArea = new Rect(ix + labelW, row, fieldW, 1); - TextInput textInput = TextInput.builder() - .cursorStyle(Style.EMPTY.reversed()) - .placeholder("default") - .build(); - frame.renderStatefulWidget(textInput, portArea, portState); + if (portDialogRow == 1) { + TextInput textInput = TextInput.builder() + .cursorStyle(Style.EMPTY.reversed()) + .placeholder("default") + .build(); + frame.renderStatefulWidget(textInput, portArea, portState); + } else { + String portText = portState != null ? portState.text() : ""; + frame.renderWidget(Paragraph.from(Line.from( + Span.styled(portText.isEmpty() ? "default" : portText, + portText.isEmpty() ? Style.EMPTY.dim() : Style.EMPTY))), + portArea); + } } private void handlePortInput(KeyEvent ke) { diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java index 97d973f097ec1..88a97555ad935 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java @@ -86,6 +86,7 @@ class IntegrationInfo { final List circuitBreakers = new ArrayList<>(); int errorCount; final List errors = new CopyOnWriteArrayList<>(); + final List activity = new CopyOnWriteArrayList<>(); boolean inflightBrowseEnabled; final List inflightExchanges = new ArrayList<>(); final List meters = new ArrayList<>(); diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java index 044d1d9158d95..e8659c403ab0a 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java @@ -84,7 +84,7 @@ interface MonitorBridge { // Tab name constants static final String[] TAB_NAMES = { "Overview", "Log", "Diagram", "Routes", "Endpoints", - "HTTP", "Health", "Inspect", "Errors", "More" + "HTTP", "Health", "Inspect", "Errors", "Activity", "More" }; private final MonitorContext ctx; diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpLogPopup.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpLogPopup.java index 3924ea077e379..cbfe67e6208ac 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpLogPopup.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpLogPopup.java @@ -127,7 +127,7 @@ void render(Frame frame, Rect area) { void renderFooter(List spans) { hint(spans, TuiIcons.HINT_SCROLL, "select"); - hint(spans, "PgUp/Dn", "scroll detail"); + hint(spans, "PgUp/Dn", "detail"); hintLast(spans, "Esc", "back"); } diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryLeakTab.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryLeakTab.java index bbba3c472d6dd..983fe07f3e10d 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryLeakTab.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MemoryLeakTab.java @@ -739,7 +739,7 @@ public void renderFooter(List spans) { hint(spans, "R", "new recording"); hint(spans, "d", "mode [" + modeLabel + "]"); hint(spans, "+/-", "duration [" + duration + "s]"); - hintLast(spans, "PgUp/Dn", "scroll detail"); + hintLast(spans, "PgUp/Dn", "detail"); } default -> hintLast(spans, "Esc", "back"); } diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OverviewTab.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OverviewTab.java index d887f6d3b06e1..1972a39a1c437 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OverviewTab.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/OverviewTab.java @@ -545,7 +545,7 @@ public void render(Frame frame, Rect area) { rightCell("ROUTE", 7, Style.EMPTY.bold()), rightCell("MSG/S", 8, Style.EMPTY.bold()), centerCell(sortLabel("TOTAL", "total"), 14, sortStyle("total")), - centerCell(sortLabel("FAIL", "fail"), 10, sortStyle("fail")), + centerCell(sortLabel("FAIL", "fail"), 14, sortStyle("fail")), rightCell(timingHeader, 14, Style.EMPTY.bold()), Cell.from("")); @@ -558,7 +558,7 @@ public void render(Frame frame, Rect area) { Constraint.length(7), Constraint.length(8), Constraint.length(14), - Constraint.length(10), + Constraint.length(14), Constraint.length(14), Constraint.fill() }; diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SqlTraceTab.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SqlTraceTab.java index 55ade2ef52a28..3da8e425e7716 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SqlTraceTab.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SqlTraceTab.java @@ -388,7 +388,7 @@ public void renderFooter(List spans) { hint(spans, "Esc", "back"); hint(spans, TuiIcons.HINT_SCROLL, "navigate"); hint(spans, "Home/End", "top/end"); - hint(spans, "PgUp/Dn", "scroll detail"); + hint(spans, "PgUp/Dn", "detail"); hint(spans, "e", "edit SQL"); hint(spans, "s", "sort"); hint(spans, "w", "wrap [" + (wordWrap ? "on" : "off") + "]"); diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java index 148867b5b57ad..5c329020b9c03 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java @@ -889,7 +889,7 @@ static void parseKvArray(JsonArray arr, Map values, Map parseErrors(JsonObject root) { Object bodyObj = msg.get("body"); if (bodyObj instanceof JsonObject bodyJson) { ei.body = bodyJson.getString("value"); - ei.bodyType = bodyJson.getString("type"); + ei.bodyType = TuiHelper.shortTypeName(bodyJson.getString("type")); } else if (bodyObj != null) { ei.body = bodyObj.toString(); } @@ -1123,6 +1123,52 @@ static List parseErrors(JsonObject root) { return parsed; } + static List parseActivityEntries(JsonObject root) { + JsonArray activityList = (JsonArray) root.get("activity"); + if (activityList == null) { + return List.of(); + } + List parsed = new ArrayList<>(); + for (Object e : activityList) { + JsonObject aj = (JsonObject) e; + ActivityEntry ae = new ActivityEntry(); + Object uidObj = aj.get("uid"); + if (uidObj instanceof Number n) { + ae.uid = n.longValue(); + } + ae.exchangeId = aj.getString("exchangeId"); + ae.routeId = aj.getString("routeId"); + ae.fromRouteId = aj.getString("fromRouteId"); + Long ts = aj.getLong("timestamp"); + if (ts != null) { + ae.timestamp = ts; + } + Object elapsedObj = aj.get("elapsed"); + if (elapsedObj instanceof Number n) { + ae.elapsed = n.longValue(); + } + ae.failed = aj.getBooleanOrDefault("failed", false); + ae.fromEndpointUri = aj.getString("fromEndpointUri"); + JsonArray sends = aj.getCollection("endpointSends"); + if (sends != null) { + for (Object s : sends) { + JsonObject so = (JsonObject) s; + ActivityEntry.EndpointSendEntry se = new ActivityEntry.EndpointSendEntry(); + se.endpointUri = so.getString("endpointUri"); + se.remoteEndpoint = so.getBooleanOrDefault("remoteEndpoint", false); + Object sendElapsed = so.get("elapsed"); + if (sendElapsed instanceof Number sn) { + se.elapsed = sn.longValue(); + } + ae.endpointSends.add(se); + } + } + ae.exceptionMessage = aj.getString("exception"); + parsed.add(ae); + } + return parsed; + } + static String stringValue(Object obj) { return obj != null ? obj.toString() : null; } diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java index 588f6d965c3e9..dce61956dea81 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java @@ -37,9 +37,10 @@ class TabRegistry { static final int TAB_HEALTH = 6; static final int TAB_HISTORY = 7; static final int TAB_ERRORS = 8; - static final int TAB_MORE = 9; + static final int TAB_ACTIVITY = 9; + static final int TAB_MORE = 10; - static final int NUM_TABS = 10; + static final int NUM_TABS = 11; /** * Callbacks for operations that remain in {@link CamelMonitor} or other collaborators. @@ -53,6 +54,8 @@ interface TabCallbacks { void refreshErrorData(List pids); + void refreshActivityData(List pids); + void openMorePopup(); void closeMorePopup(); @@ -74,6 +77,7 @@ interface TabCallbacks { private HistoryTab historyTab; private CircuitBreakerTab circuitBreakerTab; private ErrorsTab errorsTab; + private ActivityTab activityTab; private MetricsTab metricsTab; private StartupTab startupTab; private ConfigurationTab configurationTab; @@ -121,6 +125,7 @@ void initTabs(MonitorContext ctx, DataRefreshService dataService, Runnable reset historyTab = new HistoryTab(ctx, dataService.traces(), dataService.traceFilePositions()); circuitBreakerTab = new CircuitBreakerTab(ctx, dataService.metrics()); errorsTab = new ErrorsTab(ctx); + activityTab = new ActivityTab(ctx); metricsTab = new MetricsTab(ctx); startupTab = new StartupTab(ctx); configurationTab = new ConfigurationTab(ctx); @@ -181,6 +186,7 @@ MonitorTab activeTab() { case TAB_HISTORY -> historyTab; case TAB_HTTP -> httpTab; case TAB_ERRORS -> errorsTab; + case TAB_ACTIVITY -> activityTab; case TAB_MORE -> activeMoreTab; default -> null; }; @@ -231,6 +237,15 @@ boolean handleTabKey(int tab, MonitorContext ctx, DataRefreshService dataService } errorsTab.onTabSelected(); } + if (tab == TAB_ACTIVITY && ctx.selectedPid != null) { + try { + long pid = Long.parseLong(ctx.selectedPid); + callbacks.refreshActivityData(List.of(pid)); + } catch (NumberFormatException e) { + // ignore + } + activityTab.onTabSelected(); + } if (tab == TAB_MORE) { callbacks.openMorePopup(); return true; @@ -302,6 +317,10 @@ ErrorsTab errorsTab() { return errorsTab; } + ActivityTab activityTab() { + return activityTab; + } + BeansTab beansTab() { return beansTab; } @@ -389,6 +408,7 @@ List allTabEntries() { entries.add(new TabEntry(icon(TAB_HEALTH), "Health", healthTab.description(), "7", TAB_HEALTH, -1)); entries.add(new TabEntry(icon(TAB_HISTORY), "Inspect", historyTab.description(), "8", TAB_HISTORY, -1)); entries.add(new TabEntry(icon(TAB_ERRORS), "Errors", errorsTab.description(), "9", TAB_ERRORS, -1)); + entries.add(new TabEntry(icon(TAB_ACTIVITY), "Activity", activityTab.description(), "", TAB_ACTIVITY, -1)); for (int i = 0; i < moreTabs.size(); i++) { MoreTab mt = moreTabs.get(i); entries.add(new TabEntry( diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIcons.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIcons.java index f662f48a78d5e..a2bad20205edf 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIcons.java +++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TuiIcons.java @@ -120,6 +120,7 @@ final class TuiIcons { static final String TAB_HEALTH = DOCTOR; static final String TAB_INSPECT = GO_TO; static final String TAB_ERRORS = FAIL; + static final String TAB_ACTIVITY = "🔔"; static final String TAB_MORE = FOLDER_OPEN; // ---- More submenu tabs ---- @@ -147,7 +148,7 @@ final class TuiIcons { /** Icons for {@link TabRegistry#TAB_OVERVIEW}..{@link TabRegistry#TAB_MORE} (in order). */ static final List PRIMARY_TAB_ICONS = List.of( TAB_OVERVIEW, TAB_LOG, TAB_DIAGRAM, TAB_ROUTES, TAB_ENDPOINTS, - TAB_HTTP, TAB_HEALTH, TAB_INSPECT, TAB_ERRORS, TAB_MORE); + TAB_HTTP, TAB_HEALTH, TAB_INSPECT, TAB_ERRORS, TAB_ACTIVITY, TAB_MORE); /** Marker placed immediately before a label's keyboard-shortcut letter (Windows-style mnemonic). */ static final char MNEMONIC_MARKER = '&';