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 536fabe8f971 CAMEL-26733: camel-jbang - camel dirty --clean also
cleans the logs of previous runs (#29415)
536fabe8f971 is described below
commit 536fabe8f971f255f707f4d07a567ded2b5b481e
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun Nov 8 12:24:30 2026 +0100
CAMEL-26733: camel-jbang - camel dirty --clean also cleans the logs of
previous runs (#29415)
* CAMEL-26733: camel-jbang - camel dirty --previous: also clean the logs of
previous runs
* CAMEL-26733: camel-jbang - camel dirty --clean also cleans the logs of
previous runs, no --previous option
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 2 +-
.../ROOT/pages/camel-jbang-configuration.adoc | 7 ++-
.../pages/jbang-commands/camel-jbang-dirty.adoc | 2 +-
.../META-INF/camel-jbang-commands-metadata.json | 2 +-
.../dsl/jbang/core/commands/process/Dirty.java | 65 +++++++++++++++++-----
.../dsl/jbang/core/commands/process/DirtyTest.java | 48 ++++++++++++----
.../src/test/resources/golden/cli-help.txt | 3 +-
7 files changed, 95 insertions(+), 34 deletions(-)
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index 084e3f22476b..49418725e57c 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -5795,7 +5795,7 @@ When `camel run` ends, its log is no longer deleted. It
is kept as `~/.camel/<na
integration, with `~/.camel/<name>-previous.json`, which says when and how the
run ended. The next run of that name that
ends replaces both, and at most the last 10 MB of a log are kept. `camel log
<name> --previous` prints the log, like
`kubectl logs --previous` in Kubernetes. The log of an integration that was
killed is kept the same way, the next time a
-previous run is looked up. `camel dirty` does not count these files.
+previous run is looked up. `camel dirty --clean` cleans these files too.
=== camel-jbang - the AI tools wait for Camel to start, and read the log of
the previous run
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-configuration.adoc
b/docs/user-manual/modules/ROOT/pages/camel-jbang-configuration.adoc
index 68aee5cfb6dd..40ee90b06334 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-configuration.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-configuration.adoc
@@ -140,11 +140,12 @@ camel dirty
camel dirty --clean
----
-Only the files named after the pid of a process that is gone count, such as
`<pid>-status.json`. Other files in
+Only the files named after the pid of a process that is gone are orphan files,
such as `<pid>-status.json`. Other files in
`~/.camel`, such as the log of a Spring Boot or Quarkus app that `camel run`
runs (`<name>.log`), are left alone.
-The log of the previous run of an integration (`~/.camel/<name>-previous.log`,
see `camel log --previous`) is kept on
-purpose, so `camel dirty` leaves it.
+The log of the previous run of an integration (`~/.camel/<name>-previous.log`
and `<name>-previous.json`, see
+`camel log --previous`) is kept until it is cleaned: there is one for each
name that ever ran. `camel dirty` says how
+many integrations have one, and `camel dirty --clean` cleans them too, so read
the log of a run that failed first.
An error is printed as one line that starts with `ERROR:`. To see the stack
trace of every error, set the
environment variable `CAMEL_CLI_DEBUG` to `true`. See
xref:camel-jbang-exit-codes.adoc[Exit Codes and Output]
diff --git
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dirty.adoc
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dirty.adoc
index 71f9e1b42bd0..6e9ecf65bd93 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dirty.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dirty.adoc
@@ -19,7 +19,7 @@ camel dirty [options]
[cols="2,5,1,2",options="header"]
|===
| Option | Description | Default | Type
-| `--clean` | Clean dirty files which are no longer in use | false | boolean
+| `--clean` | Clean dirty files which are no longer in use, and the logs of
the previous runs of the integrations (camel log --previous) | false | 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 ce4287ac149b..bd02c83bf98f 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
@@ -9,7 +9,7 @@
{ "name": "debug", "fullName": "debug", "description": "Debug local Camel
integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Debug",
"options": [ { "names": "--ago", "description": "Use ago instead of yyyy-MM-dd
HH:mm:ss in timestamp.", "javaType": "boolean", "type": "boolean" }, { "names":
"--background", "description": "Run in the background", "defaultValue":
"false", "javaType": "boolean", "type": "boolean" }, { "names":
"--background-wait", "description": "How [...]
{ "name": "dependency", "fullName": "dependency", "description": "Displays
all Camel dependencies required to run", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.DependencyCommand", "options": [ {
"names": "-h,--help", "description": "Display the help and sub-commands",
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name":
"copy", "fullName": "dependency copy", "description": "Copies all Camel
dependencies required to run to a specific directory", "sourc [...]
{ "name": "dev", "fullName": "dev", "description": "Run in dev mode with
live reload", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Dev",
"options": [ { "names": "--background", "description": "Run in the background",
"defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names":
"--background-wait", "description": "How long to wait for the integration in
the background to start, before returning, in millis or as a duration such as
2m (true waits 60 secon [...]
- { "name": "dirty", "fullName": "dirty", "description": "Check if there are
dirty files from previous Camel runs that did not terminate gracefully",
"sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.Dirty",
"options": [ { "names": "--clean", "description": "Clean dirty files which are
no longer in use", "defaultValue": "false", "javaType": "boolean", "type":
"boolean" }, { "names": "-h,--help", "description": "Display the help and
sub-commands", "javaType": "boolean", " [...]
+ { "name": "dirty", "fullName": "dirty", "description": "Check if there are
dirty files from previous Camel runs that did not terminate gracefully",
"sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.Dirty",
"options": [ { "names": "--clean", "description": "Clean dirty files which are
no longer in use, and the logs of the previous runs of the integrations (camel
log --previous)", "defaultValue": "false", "javaType": "boolean", "type":
"boolean" }, { "names": "-h,--help" [...]
{ "name": "doc", "fullName": "doc", "description": "Shows documentation
for kamelet, component, EIP, and other Camel resources", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.catalog.CatalogDoc", "options": [ {
"names": "--camel-version", "description": "To use 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 int [...]
{ "name": "doctor", "fullName": "doctor", "description": "Checks the
environment and reports potential issues", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.Doctor", "options": [ { "names":
"--json", "description": "Output in JSON Format: the status (OK, WARN or FAIL)
of each check", "javaType": "boolean", "type": "boolean" }, { "names":
"-h,--help", "description": "Display the help and sub-commands", "javaType":
"boolean", "type": "boolean" } ] },
{ "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 [...]
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/Dirty.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/Dirty.java
index 29069073f802..d6d81189cee1 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/Dirty.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/Dirty.java
@@ -36,9 +36,13 @@ import static
org.apache.camel.dsl.jbang.core.common.CommandLineHelper.getCamelD
public class Dirty extends ProcessBaseCommand {
@CommandLine.Option(names = { "--clean" }, defaultValue = "false",
- description = "Clean dirty files which are no longer
in use")
+ description = "Clean dirty files which are no longer
in use, and the logs of the previous"
+ + " runs of the integrations (camel log
--previous)")
boolean clean;
+ // the log a log4j configuration of the CLI writes when the pid system
property is not set yet
+ static final String STRAY_LOG = "${sys:pid}.log";
+
public Dirty(CamelJBangMain main) {
super(main);
}
@@ -51,9 +55,19 @@ public class Dirty extends ProcessBaseCommand {
}
List<File> dirty = new ArrayList<>();
+ List<File> previousRuns = new ArrayList<>();
for (File f : files) {
if (PreviousRun.isPreviousRunFile(f.getName())) {
- // kept on purpose: the log of the previous run of an
integration (camel log --previous)
+ // the log of the previous run of an integration (camel log
--previous), one for each name that ever
+ // ran: kept until it is cleaned
+ if (f.isFile()) {
+ previousRuns.add(f);
+ }
+ continue;
+ }
+ if (STRAY_LOG.equals(f.getName())) {
+ // the log of a logging configuration whose ${sys:pid} was not
set, which no process writes again
+ dirty.add(f);
continue;
}
// only the files of a process that is gone: a file named after
its pid. The files of a process that is
@@ -65,31 +79,52 @@ public class Dirty extends ProcessBaseCommand {
dirty.add(f);
}
}
- if (dirty.isEmpty()) {
- return 0;
- }
-
if (clean) {
- int cleaned = 0;
List<String> left = new ArrayList<>();
- for (File f : dirty) {
- if (FileUtil.deleteFile(f)) {
- cleaned++;
- } else {
- left.add(f.getName());
- }
+ if (!dirty.isEmpty()) {
+ printer().println("Cleaned " + delete(dirty, left) + " orphan
files");
+ }
+ if (!previousRuns.isEmpty()) {
+ int runs = runs(previousRuns);
+ int cleaned = delete(previousRuns, left);
+ printer().println("Cleaned " + cleaned + " files of the
previous runs of " + runs + " integrations");
}
- printer().println("Cleaned " + cleaned + " orphan files");
if (!left.isEmpty()) {
printer().printWarn("Cannot delete: " + String.join(", ",
left));
}
} else {
- printer().println("There are " + dirty.size() + " orphan files");
+ if (!dirty.isEmpty()) {
+ printer().println("There are " + dirty.size() + " orphan
files");
+ }
+ if (!previousRuns.isEmpty()) {
+ printer().println("There are logs of the previous runs of " +
runs(previousRuns)
+ + " integrations (camel dirty --clean cleans
them)");
+ }
}
return 0;
}
+ /** Deletes the files, and returns how many; those that cannot be deleted
are added to left. */
+ private static int delete(List<File> files, List<String> left) {
+ int cleaned = 0;
+ for (File f : files) {
+ if (FileUtil.deleteFile(f)) {
+ cleaned++;
+ } else {
+ left.add(f.getName());
+ }
+ }
+ return cleaned;
+ }
+
+ /** How many integrations the files of previous runs are of: a log and
what it says about the run, per name. */
+ private static int runs(List<File> previousRuns) {
+ return (int) previousRuns.stream()
+ .map(f -> f.getName().substring(0,
f.getName().lastIndexOf('-')))
+ .distinct().count();
+ }
+
/**
* The pid a file in the Camel directory belongs to, such as 123 for
{@code 123-status.json}, {@code 123} and the
* infra service file {@code infra-kafka-123.json}, or <tt>null</tt> for a
file that belongs to no process.
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/process/DirtyTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/process/DirtyTest.java
index 85c2629710de..52d1ef98ecc9 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/process/DirtyTest.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/process/DirtyTest.java
@@ -105,23 +105,47 @@ class DirtyTest extends ProcessCommandTestSupport {
}
@Test
- void testTheLogOfAPreviousRunIsNotDirty() throws Exception {
- // kept on purpose (CAMEL-25784), and the next run of that name
replaces it
-
Files.writeString(CommandLineHelper.getCamelDir().resolve("orders-previous.log"),
"Started");
-
Files.writeString(CommandLineHelper.getCamelDir().resolve("orders-previous.json"),
"{}");
- // a name can be a number
-
Files.writeString(CommandLineHelper.getCamelDir().resolve("123-previous.log"),
"Started");
+ void testCleanCleansTheLogsOfPreviousRuns() throws Exception {
+ Path dir = CommandLineHelper.getCamelDir();
+ Files.writeString(dir.resolve("orders-previous.log"), "Started");
+ Files.writeString(dir.resolve("orders-previous.json"), "{}");
+ // a name can be a number: a previous run, not the files of pid 123
+ Files.writeString(dir.resolve("123-previous.log"), "Started");
+ Files.writeString(dir.resolve("billing-previous.log"), "Started");
+ Files.writeString(dir.resolve("billing-previous.json"), "{}");
+
+ try (MockedStatic<ProcessHandle> mocked =
mockStatic(ProcessHandle.class)) {
+ // without --clean it says how many there are, and how to clean
them
+ assertEquals(0, new Dirty(new
CamelJBangMain().withPrinter(printer)).doCall());
+ assertEquals("There are logs of the previous runs of 3
integrations (camel dirty --clean cleans them)",
+ printer.getOutput().trim());
+ assertTrue(Files.exists(dir.resolve("orders-previous.log")));
+
+ Dirty command = new Dirty(new
CamelJBangMain().withPrinter(printer));
+ command.clean = true;
+ assertEquals(0, command.doCall());
+ assertTrue(printer.getOutput().contains("Cleaned 5 files of the
previous runs of 3 integrations"),
+ printer.getOutput());
+ for (String name : new String[] {
+ "orders-previous.log", "orders-previous.json",
"billing-previous.log", "billing-previous.json",
+ "123-previous.log" }) {
+ assertFalse(Files.exists(dir.resolve(name)), name + " is
cleaned");
+ }
+ }
+ }
+
+ @Test
+ void testTheLogOfAnUnsetPidIsAnOrphan() throws Exception {
+ Path stray = CommandLineHelper.getCamelDir().resolve("${sys:pid}.log");
+ Files.writeString(stray, "WARN");
Dirty command = new Dirty(new CamelJBangMain().withPrinter(printer));
command.clean = true;
try (MockedStatic<ProcessHandle> mocked =
mockStatic(ProcessHandle.class)) {
- int exit = command.doCall();
-
- assertEquals(0, exit);
- assertEquals("", printer.getOutput().trim());
-
assertTrue(Files.exists(CommandLineHelper.getCamelDir().resolve("orders-previous.log")));
-
assertTrue(Files.exists(CommandLineHelper.getCamelDir().resolve("123-previous.log")));
+ assertEquals(0, command.doCall());
+ assertEquals("Cleaned 1 orphan files", printer.getOutput().trim());
+ assertFalse(Files.exists(stray));
}
}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/cli-help.txt
b/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/cli-help.txt
index 51a4c91f3dc1..94bfdba79733 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/cli-help.txt
+++ b/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/cli-help.txt
@@ -3019,7 +3019,8 @@ Examples:
Usage: camel dirty [-h] [--clean]
Check if there are dirty files from previous Camel runs that did not terminate
gracefully
- --clean Clean dirty files which are no longer in use
+ --clean Clean dirty files which are no longer in use, and the logs of
+ the previous runs of the integrations (camel log --previous)
-h, --help Display the help and sub-commands
Examples: