This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 73c798e8e68 CAMEL-21150: camel-jbang - Make tracer in standby as
default (#15383)
73c798e8e68 is described below
commit 73c798e8e686f5bcdd035d0800ccfb5143bd7874
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun Sep 1 07:51:20 2024 +0200
CAMEL-21150: camel-jbang - Make tracer in standby as default (#15383)
* CAMEL-21150: camel-jbang - Make tracer in standby as default
---
.../apache/camel/impl/console/TraceDevConsole.java | 77 ++++++++---
.../org/apache/camel/main/ProfileConfigurer.java | 8 +-
.../camel/management/BacklogDebuggerTest.java | 2 +-
.../org/apache/camel/support/MessageHelper.java | 6 +-
.../ROOT/pages/camel-4x-upgrade-guide-4_8.adoc | 4 +
.../modules/ROOT/pages/camel-jbang.adoc | 37 ++++-
.../camel/cli/connector/LocalCliConnector.java | 35 ++++-
.../core/commands/action/CamelTraceAction.java | 150 +++++++++++++++++++++
8 files changed, 290 insertions(+), 29 deletions(-)
diff --git
a/core/camel-console/src/main/java/org/apache/camel/impl/console/TraceDevConsole.java
b/core/camel-console/src/main/java/org/apache/camel/impl/console/TraceDevConsole.java
index c5de210a50d..f34766bb255 100644
---
a/core/camel-console/src/main/java/org/apache/camel/impl/console/TraceDevConsole.java
+++
b/core/camel-console/src/main/java/org/apache/camel/impl/console/TraceDevConsole.java
@@ -42,6 +42,11 @@ public class TraceDevConsole extends AbstractDevConsole {
*/
public static final String ENABLED = "enabled";
+ /**
+ * Whether to dump trace messages
+ */
+ public static final String DUMP = "dump";
+
private Queue<BacklogTracerEventMessage> queue;
public TraceDevConsole() {
@@ -67,17 +72,11 @@ public class TraceDevConsole extends AbstractDevConsole {
protected String doCallText(Map<String, Object> options) {
StringBuilder sb = new StringBuilder();
String enabled = (String) options.get(ENABLED);
+ String dump = (String) options.get(DUMP);
BacklogTracer tracer =
getCamelContext().getCamelContextExtension().getContextPlugin(BacklogTracer.class);
if (tracer != null) {
- if ("true".equals(enabled)) {
- tracer.setEnabled(true);
- sb.append("Enabled: ").append(tracer.isEnabled()).append("\n");
- } else if ("false".equals(enabled)) {
- tracer.setEnabled(false);
- sb.append("Enabled: ").append(tracer.isEnabled()).append("\n");
- } else {
- sb.append("Enabled: ").append(tracer.isEnabled()).append("\n");
+ if (dump != null) {
for (BacklogTracerEventMessage t :
tracer.dumpAllTracedMessages()) {
addMessage(t);
}
@@ -85,6 +84,32 @@ public class TraceDevConsole extends AbstractDevConsole {
String json = t.toJSon(0);
sb.append(json).append("\n");
}
+ } else {
+ if ("true".equals(enabled)) {
+ tracer.setEnabled(true);
+ } else if ("false".equals(enabled)) {
+ tracer.setEnabled(false);
+ }
+ sb.append("Enabled: ").append(tracer.isEnabled()).append("\n");
+ sb.append("Standby: ").append(tracer.isStandby()).append("\n");
+ sb.append("Trace Counter:
").append(tracer.getTraceCounter()).append("\n");
+ sb.append("Backlog Size:
").append(tracer.getBacklogSize()).append("\n");
+ sb.append("Queue Size:
").append(tracer.getQueueSize()).append("\n");
+ sb.append("Remove On Dump:
").append(tracer.isRemoveOnDump()).append("\n");
+ if (tracer.getTraceFilter() != null) {
+ sb.append("Trace Filter:
").append(tracer.getTraceFilter()).append("\n");
+ }
+ if (tracer.getTracePattern() != null) {
+ sb.append("Trace Pattern:
").append(tracer.getTracePattern()).append("\n");
+ }
+ sb.append("Trace Rests:
").append(tracer.isTraceRests()).append("\n");
+ sb.append("Trace Templates:
").append(tracer.isTraceTemplates()).append("\n");
+ sb.append("Body Max Chars:
").append(tracer.getBodyMaxChars()).append("\n");
+ sb.append("Body Include Files:
").append(tracer.isBodyIncludeFiles()).append("\n");
+ sb.append("Body Include Streams:
").append(tracer.isBodyIncludeStreams()).append("\n");
+ sb.append("Include Exchange Properties:
").append(tracer.isIncludeExchangeProperties()).append("\n");
+ sb.append("Include Exchange Variables:
").append(tracer.isIncludeExchangeVariables()).append("\n");
+ sb.append("Include Exception:
").append(tracer.isIncludeException()).append("\n");
}
}
@@ -105,20 +130,14 @@ public class TraceDevConsole extends AbstractDevConsole {
protected JsonObject doCallJson(Map<String, Object> options) {
JsonObject root = new JsonObject();
String enabled = (String) options.get(ENABLED);
+ String dump = (String) options.get(DUMP);
BacklogTracer tracer =
getCamelContext().getCamelContextExtension().getContextPlugin(BacklogTracer.class);
if (tracer != null) {
- if ("true".equals(enabled)) {
- tracer.setEnabled(true);
- root.put("enabled", tracer.isEnabled());
- } else if ("false".equals(enabled)) {
- tracer.setEnabled(false);
- root.put("enabled", tracer.isEnabled());
- } else {
+ if (dump != null) {
for (BacklogTracerEventMessage t :
tracer.dumpAllTracedMessages()) {
addMessage(t);
}
-
JsonArray arr = new JsonArray();
root.put("enabled", tracer.isEnabled());
root.put("traces", arr);
@@ -126,6 +145,32 @@ public class TraceDevConsole extends AbstractDevConsole {
JsonObject jo = (JsonObject) t.asJSon();
arr.add(jo);
}
+ } else {
+ if ("true".equals(enabled)) {
+ tracer.setEnabled(true);
+ } else if ("false".equals(enabled)) {
+ tracer.setEnabled(false);
+ }
+ root.put("enabled", tracer.isEnabled());
+ root.put("standby", tracer.isStandby());
+ root.put("counter", tracer.getTraceCounter());
+ root.put("backlogSize", tracer.getBacklogSize());
+ root.put("queueSize", tracer.getQueueSize());
+ root.put("removeOnDump", tracer.isRemoveOnDump());
+ if (tracer.getTraceFilter() != null) {
+ root.put("traceFilter", tracer.getTraceFilter());
+ }
+ if (tracer.getTracePattern() != null) {
+ root.put("tracePattern", tracer.getTracePattern());
+ }
+ root.put("traceRests", tracer.isTraceRests());
+ root.put("traceTemplates", tracer.isTraceTemplates());
+ root.put("bodyMaxChars", tracer.getBodyMaxChars());
+ root.put("bodyIncludeFiles", tracer.isBodyIncludeFiles());
+ root.put("bodyIncludeStreams", tracer.isBodyIncludeStreams());
+ root.put("includeExchangeProperties",
tracer.isIncludeExchangeProperties());
+ root.put("includeExchangeVariables",
tracer.isIncludeExchangeVariables());
+ root.put("includeException", tracer.isIncludeException());
}
}
diff --git
a/core/camel-main/src/main/java/org/apache/camel/main/ProfileConfigurer.java
b/core/camel-main/src/main/java/org/apache/camel/main/ProfileConfigurer.java
index 3973098923e..aa0b128ac53 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/ProfileConfigurer.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/ProfileConfigurer.java
@@ -44,10 +44,10 @@ public class ProfileConfigurer {
}
if ("dev".equals(profile)) {
- boolean standby = config.tracerConfig().isStandby();
- if (!standby) {
- // make tracing enabled (if not configured to be standby) and
limit to not capture too much data
- config.tracerConfig().withEnabled(true);
+ // make tracing at least standby so we can use it in dev-mode
+ boolean enabled = config.tracerConfig().isEnabled();
+ if (!enabled) {
+ config.tracerConfig().withStandby(true);
}
}
diff --git
a/core/camel-management/src/test/java/org/apache/camel/management/BacklogDebuggerTest.java
b/core/camel-management/src/test/java/org/apache/camel/management/BacklogDebuggerTest.java
index 5af09dd12a4..458ff9ab4da 100644
---
a/core/camel-management/src/test/java/org/apache/camel/management/BacklogDebuggerTest.java
+++
b/core/camel-management/src/test/java/org/apache/camel/management/BacklogDebuggerTest.java
@@ -349,10 +349,10 @@ public class BacklogDebuggerTest extends
ManagementTestSupport {
assertNotNull(xml);
log.info(xml);
- assertTrue(xml.contains("<body>[Body is null]</body>"), "Should not
contain our body");
assertTrue(xml.contains("<toNode>bar</toNode>"), "Should contain bar
node");
assertFalse(xml.contains("<header"), "Should not contain any headers");
assertFalse(xml.contains("<exchangeProperty key=\"food\""), "Should
not contain exchange property 'food'");
+ assertTrue(xml.contains("<body></body>"), "Should not contain our
body");
resetMocks();
mock.expectedMessageCount(1);
diff --git
a/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
b/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
index 9bc464ccabe..c0987d91feb 100644
---
a/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
+++
b/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
@@ -1102,7 +1102,11 @@ public final class MessageHelper {
}
String data = extractBodyForLogging(message, null,
allowCachedStreams, allowStreams, allowFiles, maxChars);
if (data != null) {
- jb.put("value", Jsoner.escape(data));
+ if ("[Body is null]".equals(data)) {
+ jb.put("value", null);
+ } else {
+ jb.put("value", Jsoner.escape(data));
+ }
}
}
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_8.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_8.adoc
index 24fbc24157e..25c92369df7 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_8.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_8.adoc
@@ -25,6 +25,10 @@ performing these copies has increased from 4096 bytes to
16384 bytes (the defaul
The tracer (`BacklogTracer`) has changed the `backlogSize` default value from
`1000` to `100`, and `maxBodySize` from `128kb` to `32kb`.
This reduces the amount of data captured and stored and helps reduce the
tracing overhead.
+=== camel-jbang
+
+The `camel trace` command has changed to show tracing status (by default). To
dump traced messages use `camel trace --action=dump`.
+
=== Deprecated Components
The following components that were marked as deprecated:
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
index da83b483961..1a201c620f6 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
@@ -1611,20 +1611,51 @@ command but only display message tracing information.
This allows you to see eve
The `trace` command has many options and can be used to _filter_, _grep_ or
output on different detail _levels`.
The _exchange id_ is logged (and grouped by colour), so you can use that to
correlate the events, when traces are interleaved.
-For example if an existing integration is running named chuck, you can trace
it as follows:
+The trace command will by default list the status of whether tracing is
enabled or not in the integrations:
[source,bash]
----
-camel trace chuck
+camel trace
+ PID NAME AGE STATUS TOTAL QUEUE FILTER PATTERN
+ 6911 chuck 5s Standby 0 0
+----
+
+Here we can see that the tracer is in standby mode, and you need to start the
tracer before Camel will capture messages:
+
+TIP: Camel 4.8 onwards has tracing in standby mode (when using dev profile).
You can enable tracing on startup by setting the configuration
`camel.trace.enabled=true` in `application.properties`.
+
+[source,bash]
+----
+camel trace --action=start
----
-You can also trace all running integrations
+And if you run `camel trace` again you can see the tracer is started:
[source,bash]
----
camel trace
+PID NAME AGE STATUS TOTAL QUEUE FILTER PATTERN
+6911 chuck 1m5s Started 16 4
----
+And to show the traces you need to use the `dump` action as follows:
+
+[source,bash]
+----
+camel trace chuck --action=dump
+----
+
+You can also dump traces from all running integrations:
+
+[source,bash]
+----
+camel trace --action=dump
+----
+
+To stop tracing use `--action=stop`.
+
+And you can also clear the already traced messages with `--action=clear`.
+
==== Running Camel integrations in background
The `run` command allows to run Camel in the background with the
`--background` option.
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 1620d294c44..31b30118cd8 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
@@ -274,6 +274,8 @@ public class LocalCliConnector extends ServiceSupport
implements CliConnector, C
doActionBeanTask(root);
} else if ("kafka".equals(action)) {
doActionKafkaTask();
+ } else if ("trace".equals(action)) {
+ doActionTraceTask(root);
}
} catch (Exception e) {
// ignore
@@ -733,6 +735,24 @@ public class LocalCliConnector extends ServiceSupport
implements CliConnector, C
}
}
+ private void doActionTraceTask(JsonObject root) throws IOException {
+ DevConsole dc =
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
+ .resolveById("trace");
+ if (dc != null) {
+ String enabled = root.getString("enabled");
+ JsonObject json;
+ if (enabled != null) {
+ json = (JsonObject) dc.call(DevConsole.MediaType.JSON,
Map.of("enabled", enabled));
+ } else {
+ json = (JsonObject) dc.call(DevConsole.MediaType.JSON);
+ }
+ LOG.trace("Updating output file: {}", outputFile);
+ IOHelper.writeText(json.toJson(), outputFile);
+ } else {
+ IOHelper.writeText("{}", outputFile);
+ }
+ }
+
private void doActionBeanTask(JsonObject root) throws IOException {
String filter = root.getStringOrDefault("filter", "");
String properties = root.getStringOrDefault("properties", "true");
@@ -977,13 +997,20 @@ public class LocalCliConnector extends ServiceSupport
implements CliConnector, C
root.put("fault-tolerance", json);
}
}
- DevConsole dc12a = dcr.resolveById("circuit-breaker");
- if (dc12a != null) {
- JsonObject json = (JsonObject)
dc12a.call(DevConsole.MediaType.JSON);
+ DevConsole dc12 = dcr.resolveById("circuit-breaker");
+ if (dc12 != null) {
+ JsonObject json = (JsonObject)
dc12.call(DevConsole.MediaType.JSON);
if (json != null && !json.isEmpty()) {
root.put("circuit-breaker", json);
}
}
+ DevConsole dc13 = dcr.resolveById("trace");
+ if (dc13 != null) {
+ JsonObject json = (JsonObject)
dc13.call(DevConsole.MediaType.JSON);
+ if (json != null && !json.isEmpty()) {
+ root.put("trace", json);
+ }
+ }
DevConsole dc14 = dcr.resolveById("consumer");
if (dc14 != null) {
JsonObject json = (JsonObject)
dc14.call(DevConsole.MediaType.JSON);
@@ -1069,7 +1096,7 @@ public class LocalCliConnector extends ServiceSupport
implements CliConnector, C
DevConsole dc12 =
camelContext.getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
.resolveById("trace");
if (dc12 != null) {
- JsonObject json = (JsonObject)
dc12.call(DevConsole.MediaType.JSON);
+ JsonObject json = (JsonObject)
dc12.call(DevConsole.MediaType.JSON, Map.of("dump", "true"));
JsonArray arr = json.getCollection("traces");
// filter based on last uid
if (traceFilePos > 0) {
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java
index 28e5b962b10..bf569701a5b 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelTraceAction.java
@@ -23,6 +23,7 @@ import java.io.LineNumberReader;
import java.text.SimpleDateFormat;
import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -35,8 +36,13 @@ import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.regex.Pattern;
+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.catalog.impl.TimePatternConverter;
import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.common.PidNameAgeCompletionCandidates;
import org.apache.camel.dsl.jbang.core.common.ProcessHelper;
import org.apache.camel.util.IOHelper;
import org.apache.camel.util.StopWatch;
@@ -68,9 +74,29 @@ public class CamelTraceAction extends ActionBaseCommand {
}
}
+ public static class ActionCompletionCandidates implements Iterable<String>
{
+
+ public ActionCompletionCandidates() {
+ }
+
+ @Override
+ public Iterator<String> iterator() {
+ return List.of("dump", "start", "stop", "status",
"clear").iterator();
+ }
+ }
+
@CommandLine.Parameters(description = "Name or pid of running Camel
integration. (default selects all)", arity = "0..1")
String name = "*";
+ @CommandLine.Option(names = { "--action" }, completionCandidates =
ActionCompletionCandidates.class,
+ defaultValue = "status",
+ description = "Action to start, stop, clear, list
status, or dump traces")
+ String action;
+
+ @CommandLine.Option(names = { "--sort" }, completionCandidates =
PidNameAgeCompletionCandidates.class,
+ description = "Sort by pid, name or age for showing
status of tracing", defaultValue = "pid")
+ String sort;
+
@CommandLine.Option(names = { "--timestamp" }, defaultValue = "true",
description = "Print timestamp.")
boolean timestamp = true;
@@ -167,6 +193,117 @@ public class CamelTraceAction extends ActionBaseCommand {
@Override
public Integer doCall() throws Exception {
+ if ("dump".equals(action)) {
+ return doDumpCall();
+ } else if ("status".equals(action)) {
+ return doStatusCall();
+ }
+
+ List<Long> pids = findPids(name);
+ for (long pid : pids) {
+ if ("clear".equals(action)) {
+ File f = getTraceFile("" + pid);
+ if (f.exists()) {
+ IOHelper.writeText("{}", f);
+ }
+ } else {
+ JsonObject root = new JsonObject();
+ root.put("action", "trace");
+ if ("start".equals(action)) {
+ root.put("enabled", "true");
+ } else if ("stop".equals(action)) {
+ root.put("enabled", "false");
+ }
+ File f = getActionFile(Long.toString(pid));
+ IOHelper.writeText(root.toJson(), f);
+ }
+ }
+
+ return 0;
+ }
+
+ protected Integer doStatusCall() {
+ List<StatusRow> rows = new ArrayList<>();
+
+ List<Long> pids = findPids(name);
+ ProcessHandle.allProcesses()
+ .filter(ph -> pids.contains(ph.pid()))
+ .forEach(ph -> {
+ JsonObject root = loadStatus(ph.pid());
+ if (root != null) {
+ StatusRow row = new StatusRow();
+ JsonObject context = (JsonObject) root.get("context");
+ if (context == null) {
+ return;
+ }
+ row.name = context.getString("name");
+ if ("CamelJBang".equals(row.name)) {
+ row.name = ProcessHelper.extractName(root, ph);
+ }
+ row.pid = Long.toString(ph.pid());
+ row.uptime = extractSince(ph);
+ row.age = TimeUtils.printSince(row.uptime);
+ JsonObject jo = root.getMap("trace");
+ if (jo != null) {
+ row.enabled = jo.getBoolean("enabled");
+ row.standby = jo.getBoolean("standby");
+ row.counter = jo.getLong("counter");
+ row.queueSize = jo.getLong("queueSize");
+ row.filter = jo.getString("traceFilter");
+ row.pattern = jo.getString("tracePattern");
+ }
+ rows.add(row);
+ }
+ });
+
+ // sort rows
+ rows.sort(this::sortStatusRow);
+
+ if (!rows.isEmpty()) {
+ 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("STATUS").with(this::getTraceStatus),
+ new Column().header("TOTAL").with(r -> "" + r.counter),
+ new Column().header("QUEUE").with(r -> "" + r.queueSize),
+ new Column().header("FILTER").with(r -> r.filter),
+ new Column().header("PATTERN").with(r -> r.pattern))));
+ }
+
+ return 0;
+ }
+
+ private String getTraceStatus(StatusRow r) {
+ if (r.enabled) {
+ return "Started";
+ } else if (r.standby) {
+ return "Standby";
+ }
+ return "Stopped";
+ }
+
+ protected int sortStatusRow(StatusRow o1, StatusRow o2) {
+ String s = sort;
+ int negate = 1;
+ if (s.startsWith("-")) {
+ s = s.substring(1);
+ negate = -1;
+ }
+ switch (s) {
+ case "pid":
+ return Long.compare(Long.parseLong(o1.pid),
Long.parseLong(o2.pid)) * negate;
+ case "name":
+ return o1.name.compareToIgnoreCase(o2.name) * negate;
+ case "age":
+ return Long.compare(o1.uptime, o2.uptime) * negate;
+ default:
+ return 0;
+ }
+ }
+
+ protected Integer doDumpCall() throws Exception {
// setup table helper
tableHelper = new MessageTableHelper();
tableHelper.setPretty(pretty);
@@ -825,4 +962,17 @@ public class CamelTraceAction extends ActionBaseCommand {
}
+ private static class StatusRow {
+ String pid;
+ String name;
+ String age;
+ long uptime;
+ boolean enabled;
+ boolean standby;
+ long counter;
+ long queueSize;
+ String filter;
+ String pattern;
+ }
+
}