This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch CAMEL-23617 in repository https://gitbox.apache.org/repos/asf/camel.git
commit c7a0bdf6fb214f8ca50d69817b8aaa12a17b5a1d Author: Claus Ibsen <[email protected]> AuthorDate: Tue May 26 16:33:05 2026 +0200 CAMEL-23617: Add message-size bundled example and fix Content-Length fallback Co-Authored-By: Claude Opus 4.6 <[email protected]> --- .../main/camel-main-configuration-metadata.json | 1 + .../impl/engine/DefaultMessageSizeStrategy.java | 6 +- .../jbang-commands/camel-jbang-get-endpoint.adoc | 3 +- .../META-INF/camel-jbang-commands-metadata.json | 2 +- .../jbang/core/commands/process/ListEndpoint.java | 5 +- .../examples/camel-jbang-example-catalog.json | 18 ++++ .../main/resources/examples/message-size/README.md | 57 ++++++++++++ .../examples/message-size/message-size.camel.yaml | 102 +++++++++++++++++++++ 8 files changed, 187 insertions(+), 7 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json index 9e39a3ecdeff..a29587c0c21e 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json @@ -104,6 +104,7 @@ { "name": "camel.main.mainListeners", "required": false, "description": "Sets main listener objects that will be used for MainListener that makes it possible to do custom logic during starting and stopping camel-main.", "sourceType": "org.apache.camel.main.MainConfigurationProperties", "type": "array", "javaType": "java.util.List", "secret": false }, { "name": "camel.main.mdcLoggingKeysPattern", "required": false, "description": "Sets the pattern used for determine which custom MDC keys to propagate during message routing when the routing engine continues routing asynchronously for the given message. Setting this pattern to will propagate all custom keys. Or setting the pattern to foo,bar will propagate any keys starting with either foo or bar. Notice that a set of standard Camel MDC keys are always propagated which starts with c [...] { "name": "camel.main.messageHistory", "required": false, "description": "Sets whether message history is enabled or not. Default is false.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": false, "secret": false }, + { "name": "camel.main.messageSizeEnabled", "required": false, "description": "Sets whether message size observation is enabled (default is false). When enabled, Camel will compute the size of incoming message body and headers (in bytes) per route and make this available via JMX MBeans (min\/max\/mean body size and headers size).", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": false, "secret": false }, { "name": "camel.main.modeline", "required": false, "description": "Whether to support JBang style \/\/DEPS to specify additional dependencies when running Camel JBang", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": false, "secret": false }, { "name": "camel.main.name", "required": false, "description": "Sets the name of the CamelContext.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "string", "javaType": "java.lang.String", "secret": false }, { "name": "camel.main.producerTemplateCacheSize", "required": false, "description": "Producer template endpoints cache size.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "integer", "javaType": "int", "defaultValue": 1000, "secret": false }, diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultMessageSizeStrategy.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultMessageSizeStrategy.java index 4d2240960ade..2d2a94adf21f 100644 --- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultMessageSizeStrategy.java +++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultMessageSizeStrategy.java @@ -62,9 +62,6 @@ public class DefaultMessageSizeStrategy extends ServiceSupport implements CamelC @Override public long computeBodySize(Message message) { Object body = message.getBody(); - if (body == null) { - return 0; - } if (body instanceof byte[] bytes) { return bytes.length; } @@ -94,6 +91,9 @@ public class DefaultMessageSizeStrategy extends ServiceSupport implements CamelC if (cl != null && cl >= 0) { return cl; } + if (body == null) { + return 0; + } return -1; } diff --git a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-endpoint.adoc b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-endpoint.adoc index f8d3f58e1600..5bf9bb5be3d0 100644 --- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-endpoint.adoc +++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-endpoint.adoc @@ -25,7 +25,8 @@ camel get endpoint [options] | `--json` | Output in JSON Format | | boolean | `--limit` | Filter endpoints 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 or total | pid | String +| `--sort` | Sort by pid, name, age, total, or size | pid | String +| `--verbose` | Show additional size statistics (min/max body and headers) | | boolean | `--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/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 fdc22c91dc6c..194ec512ccdc 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 @@ -14,7 +14,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.EvalEx [...] { "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' (A [...] { "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", "ty [...] - { "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 [...] + { "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 [...] { "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", [...] { "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": [...] { "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 [...] diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListEndpoint.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListEndpoint.java index 85449071e587..5059593809af 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListEndpoint.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListEndpoint.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.stream.Collectors; import com.github.freva.asciitable.AsciiTable; @@ -262,9 +263,9 @@ public class ListEndpoint extends ProcessWatchCommand { if (size < 1024) { return size + " B"; } else if (size < 1024 * 1024) { - return String.format("%.1f KB", size / 1024.0); + return String.format(Locale.US, "%.1f KB", size / 1024.0); } else { - return String.format("%.1f MB", size / (1024.0 * 1024.0)); + return String.format(Locale.US, "%.1f MB", size / (1024.0 * 1024.0)); } } diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/camel-jbang-example-catalog.json b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/camel-jbang-example-catalog.json index 4e42f22e97d8..fca58c3f0788 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/camel-jbang-example-catalog.json +++ b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/camel-jbang-example-catalog.json @@ -226,6 +226,24 @@ "rest-api.camel.yaml" ] }, + { + "name": "message-size", + "title": "Message Size", + "description": "Track message body and header sizes per endpoint", + "level": "beginner", + "tags": [ + "observability", + "monitoring", + "seda" + ], + "bundled": true, + "requiresDocker": false, + "hasCitrusTests": false, + "files": [ + "README.md", + "message-size.camel.yaml" + ] + }, { "name": "mqtt", "title": "MQTT", diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/message-size/README.md b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/message-size/README.md new file mode 100644 index 000000000000..5332bdf435c9 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/message-size/README.md @@ -0,0 +1,57 @@ +## Message Size + +This example demonstrates Camel's message size tracking feature, which captures +body and header sizes per endpoint for both incoming (IN) and outgoing (OUT) directions. + +Three timer-driven producers simulate messages of different sizes (small, medium, large) +using the `Content-Length` header and send them to separate SEDA endpoints. +The size statistics (min, max, mean) are tracked per endpoint and can be viewed via the CLI. + +### How to run + +```sh +$ camel run * +``` + +### Viewing message size statistics + +While the integration is running, open another terminal and use the `camel` CLI +to view endpoint statistics including message sizes: + +```sh +$ camel get endpoint +``` + +To see detailed min/max statistics: + +```sh +$ camel get endpoint --verbose +``` + +To sort endpoints by body size (largest first): + +```sh +$ camel get endpoint --sort -size +``` + +### How it works + +Message size tracking is automatically enabled when running with `camel run` +which uses the dev profile. This sets: + +- `camel.main.messageSizeEnabled = true` +- `camel.main.jmxManagementStatisticsLevel = Extended` + +Sizes are tracked per endpoint in the runtime endpoint registry. For incoming messages, +the body and headers sizes are also available as exchange properties +(`CamelMessageBodySize` and `CamelMessageHeadersSize`) during routing. + +### Help and contributions + +If you hit any problem using Camel or have some feedback, then please +[let us know](https://camel.apache.org/community/support/). + +We also love contributors, so +[get involved](https://camel.apache.org/community/contributing/) :-) + +The Camel riders! diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/message-size/message-size.camel.yaml b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/message-size/message-size.camel.yaml new file mode 100644 index 000000000000..9b2e0361c3a4 --- /dev/null +++ b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/message-size/message-size.camel.yaml @@ -0,0 +1,102 @@ +# +# 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. +# + +# Producers: simulate different payload sizes using Content-Length header +- route: + id: small-producer + from: + uri: timer:small + parameters: + period: 2000 + steps: + - setHeader: + name: Content-Length + simple: "${random(100,1023)}" + - setHeader: + name: source + simple: small-producer + - to: + uri: seda:small + +- route: + id: medium-producer + from: + uri: timer:medium + parameters: + period: 3000 + steps: + - setHeader: + name: Content-Length + simple: "${random(8192,18432)}" + - setHeader: + name: source + simple: medium-producer + - setHeader: + name: tracking-id + simple: "TRK-${random(10000,99999)}" + - to: + uri: seda:medium + +- route: + id: large-producer + from: + uri: timer:large + parameters: + period: 5000 + steps: + - setHeader: + name: Content-Length + simple: "${random(10485760,20971520)}" + - setHeader: + name: source + simple: large-producer + - setHeader: + name: tracking-id + simple: "TRK-${random(10000,99999)}" + - setHeader: + name: batch-id + simple: "BATCH-${random(100,999)}" + - setHeader: + name: priority + simple: "${random(1,5)}" + - to: + uri: seda:large + +# Consumers: process messages from each seda endpoint +- route: + id: process-small + from: + uri: seda:small + steps: + - log: + message: "Small: Content-Length=${header.Content-Length}" + +- route: + id: process-medium + from: + uri: seda:medium + steps: + - log: + message: "Medium: Content-Length=${header.Content-Length}" + +- route: + id: process-large + from: + uri: seda:large + steps: + - log: + message: "Large: Content-Length=${header.Content-Length}"
