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 10ffe7c63249 CAMEL-25820: camel-jbang - the log4j configuration on the
classpath logs to the console only, and the log files go to the Camel directory
of the CLI (#28113)
10ffe7c63249 is described below
commit 10ffe7c63249aad9d143dda07a4435459db83479
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Oct 19 21:54:10 2026 +0200
CAMEL-25820: camel-jbang - the log4j configuration on the classpath logs to
the console only, and the log files go to the Camel directory of the CLI
(#28113)
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 13 +++
.../apache/camel/dsl/jbang/core/commands/Run.java | 5 +-
.../camel/dsl/jbang/core/common/RuntimeUtil.java | 21 +++-
...ckground.properties => log4j2-color.properties} | 14 ++-
.../src/main/resources/log4j2-export.properties | 3 +-
.../src/main/resources/log4j2-no-color.properties | 3 +-
.../src/main/resources/log4j2-script.properties | 3 +-
.../src/main/resources/log4j2.properties | 20 ++--
.../jbang/core/common/LogConfigurationTest.java | 111 +++++++++++++++++++++
9 files changed, 171 insertions(+), 22 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 4a521a7823f7..884216a6a6a1 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
@@ -1788,6 +1788,19 @@ a `bytes` field is now a `byte[]` with the content of
the field. Previously it w
`ByteString.toString()` returns, a description such as `<ByteString@1b6d3586
size=4 contents="...">`, which does not
contain the whole value. When a `Map` is marshalled, a `byte[]` value is
accepted for a `bytes` field.
+=== camel-jbang - where the CLI writes its log files
+
+* The `log4j2.properties` of camel-jbang-core logs to the console only. It is
the configuration that log4j finds on the
+ classpath when nothing configures logging. It also wrote a log file into
`~/.camel`, named after the `pid` system
+ property, which only the CLI sets: a JVM that uses camel-jbang-core without
the CLI (such as a test) wrote
+ `~/.camel/${sys:pid}.log`, and `camel test run` left a `<pid>.log` there.
The configuration that `camel run` uses with
+ color is now `log4j2-color.properties`, next to `log4j2-no-color.properties`.
+* The log files of the CLI (`<pid>.log`, `camel-export.log` and
`camel-script.log`) are written into the Camel directory
+ of the CLI, where `camel log` reads them, instead of `~/.camel` under
`user.home`. The two differ when `user.home` is
+ `?` (a user without a home, such as in a container), as the CLI then uses
`$HOME`, and when a test gives the CLI a
+ home of its own. The CLI sets the directory as the system property
`camel.jbang.logDir`. A Quarkus app that
+ `camel run` exports and runs also logs there.
+
=== camel-jbang-mcp - runtime tools that changed behaviour
Some runtime tools of the Camel MCP server now do what their description says,
which changes what a client sees:
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
index e6bf47779e3a..e133dd3a5675 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
@@ -1780,9 +1780,12 @@ public class Run extends CamelCommand {
Path appProps = Paths.get(eq.exportDir,
"src/main/resources/application.properties");
if (Files.exists(appProps)) {
String content = Files.readString(appProps);
+ // in the Camel directory of the CLI, where camel log looks for it
(as for an existing Quarkus project)
+ String logFile = CommandLineHelper.getCamelDir().resolve(appName +
".log").toAbsolutePath().toString()
+ .replace("\\", "/");
content += "\n# logging to file\n"
+ "quarkus.log.file.enabled=true\n"
- + "quarkus.log.file.path=${user.home}/.camel/" +
appName + ".log\n"
+ + "quarkus.log.file.path=" + logFile + "\n"
+ "quarkus.log.file.format=" + QUARKUS_LOG_FILE_FORMAT
+ "\n";
Files.writeString(appProps, content);
}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/RuntimeUtil.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/RuntimeUtil.java
index 11f565b8907b..a861013ba68f 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/RuntimeUtil.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/RuntimeUtil.java
@@ -36,6 +36,20 @@ import org.apache.logging.log4j.core.config.Configurator;
public final class RuntimeUtil {
+ /**
+ * The system property with the directory of the log files, which the
logging configurations of the CLI name their
+ * files after: the Camel directory of the CLI ({@link
CommandLineHelper#getCamelDir()}), where camel run, camel log
+ * and the AI tools look for them. It can differ from <tt>~/.camel</tt>,
such as for a CLI with a home of its own (a
+ * test), or when the user has no home (a container), where
<tt>user.home</tt> is <tt>?</tt>. The CLI sets it, it is
+ * not an option.
+ */
+ static final String LOG_DIR = "camel.jbang.logDir";
+
+ /**
+ * The logging configuration of the CLI with color: the console, and the
log file of the process.
+ */
+ static final String COLOR_CONFIG = "log4j2-color.properties";
+
// the logging configuration log4j was configured with, or null when not
yet
private static String configured;
// whether the command that runs next may configure the logging again (see
newCommand)
@@ -71,7 +85,7 @@ public final class RuntimeUtil {
} else if (json) {
name = "log4j2-json.properties";
} else if (color) {
- name = "log4j2.properties";
+ name = COLOR_CONFIG;
}
InputStream is =
RuntimeUtil.class.getClassLoader().getResourceAsStream(name);
String content = IOHelper.loadText(is);
@@ -117,12 +131,15 @@ public final class RuntimeUtil {
} else if (json) {
location = "log4j2-json.properties";
} else if (color) {
- location = "log4j2.properties";
+ location = COLOR_CONFIG;
} else {
location = "log4j2-no-color.properties";
}
synchronized (RuntimeUtil.class) {
+ // log4j resolves the name of a log file when it loads the
configuration. Always the Camel directory of the
+ // CLI, not an option: camel log and the AI tools read the log of
a pid there
+ System.setProperty(LOG_DIR,
CommandLineHelper.getCamelDir().toString());
if (configured == null) {
long pid = ProcessHandle.current().pid();
System.setProperty("pid", Long.toString(pid));
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-background.properties
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-color.properties
similarity index 68%
rename from
dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-background.properties
rename to
dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-color.properties
index 691a765a7c4a..cfbef5358e51 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-background.properties
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-color.properties
@@ -15,17 +15,25 @@
## limitations under the License.
## ---------------------------------------------------------------------------
+# console logger
+appender.stdout.type = Console
+appender.stdout.name = out
+appender.stdout.layout.type = PatternLayout
+# logging style that is similar to spring boot
+appender.stdout.layout.pattern = %style{%d{yyyy-MM-dd HH:mm:ss.SSS}}{Dim}
%highlight{%5p} %style{%pid}{Magenta} %style{---}{Dim} %style{[%15.15t]}{Dim}
%style{%-40.40c}{Cyan} %style{:}{Dim} %m%n
+
# file logger
appender.file.type = File
appender.file.name = file
-appender.file.fileName =
${sys:user.home}${sys:file.separator}.camel${sys:file.separator}${sys:pid}.log
+# in the Camel directory of the CLI (camel.jbang.logDir, which the CLI sets),
else in ~/.camel
+appender.file.fileName =
${sys:camel.jbang.logDir:-${sys:user.home}${sys:file.separator}.camel}${sys:file.separator}${sys:pid}.log
appender.file.createOnDemand = true
appender.file.append = false
appender.file.layout.type = PatternLayout
# logging style that is similar to spring boot
appender.file.layout.pattern = %style{%d{yyyy-MM-dd HH:mm:ss.SSS}}{Dim}
%highlight{%5p} %style{%pid}{Magenta} %style{---}{Dim} %style{[%15.15t]}{Dim}
%style{%-40.40c}{Cyan} %style{:}{Dim} %m%n
-# log to file
-rootLogger = INFO,file
+# log to console and file
+rootLogger = INFO,out,file
rootLogger.level = INFO
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-export.properties
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-export.properties
index 45229e36eddd..baf28cfacbd0 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-export.properties
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-export.properties
@@ -17,7 +17,8 @@
appender.file.type = File
appender.file.name = file
-appender.file.fileName =
${sys:user.home}${sys:file.separator}.camel${sys:file.separator}camel-export.log
+# in the Camel directory of the CLI (camel.jbang.logDir, which the CLI sets),
else in ~/.camel
+appender.file.fileName =
${sys:camel.jbang.logDir:-${sys:user.home}${sys:file.separator}.camel}${sys:file.separator}camel-export.log
appender.file.createOnDemand = true
appender.file.append = false
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-no-color.properties
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-no-color.properties
index 6d069aaf0abc..9bdf515fd530 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-no-color.properties
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-no-color.properties
@@ -25,7 +25,8 @@ appender.stdout.layout.pattern = %d{yyyy-MM-dd HH:mm:ss.SSS}
%5p %pid --- [%15.1
# file logger
appender.file.type = File
appender.file.name = file
-appender.file.fileName =
${sys:user.home}${sys:file.separator}.camel${sys:file.separator}${sys:pid}.log
+# in the Camel directory of the CLI (camel.jbang.logDir, which the CLI sets),
else in ~/.camel
+appender.file.fileName =
${sys:camel.jbang.logDir:-${sys:user.home}${sys:file.separator}.camel}${sys:file.separator}${sys:pid}.log
appender.file.createOnDemand = true
appender.file.append = false
appender.file.layout.type = PatternLayout
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-script.properties
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-script.properties
index fd5b03f612cb..d95df61fdc6c 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-script.properties
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2-script.properties
@@ -17,7 +17,8 @@
appender.file.type = File
appender.file.name = file
-appender.file.fileName =
${sys:user.home}${sys:file.separator}.camel${sys:file.separator}camel-script.log
+# in the Camel directory of the CLI (camel.jbang.logDir, which the CLI sets),
else in ~/.camel
+appender.file.fileName =
${sys:camel.jbang.logDir:-${sys:user.home}${sys:file.separator}.camel}${sys:file.separator}camel-script.log
appender.file.createOnDemand = true
appender.file.append = false
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2.properties
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2.properties
index db1a27651804..2558684e5dd3 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2.properties
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/resources/log4j2.properties
@@ -15,6 +15,11 @@
## limitations under the License.
## ---------------------------------------------------------------------------
+# The configuration that log4j finds on the classpath of a JVM with
camel-jbang-core when nothing configures logging,
+# such as a test of a module that depends on camel-jbang-core, or an
application that embeds it. It logs to the console
+# only: the log file of an integration is configured by the CLI
(log4j2-color.properties and log4j2-no-color.properties),
+# which also sets the pid and the directory that the file is named after.
+
# console logger
appender.stdout.type = Console
appender.stdout.name = out
@@ -22,17 +27,6 @@ appender.stdout.layout.type = PatternLayout
# logging style that is similar to spring boot
appender.stdout.layout.pattern = %style{%d{yyyy-MM-dd HH:mm:ss.SSS}}{Dim}
%highlight{%5p} %style{%pid}{Magenta} %style{---}{Dim} %style{[%15.15t]}{Dim}
%style{%-40.40c}{Cyan} %style{:}{Dim} %m%n
-# file logger
-appender.file.type = File
-appender.file.name = file
-appender.file.fileName =
${sys:user.home}${sys:file.separator}.camel${sys:file.separator}${sys:pid}.log
-appender.file.createOnDemand = true
-appender.file.append = false
-appender.file.layout.type = PatternLayout
-# logging style that is similar to spring boot
-appender.file.layout.pattern = %style{%d{yyyy-MM-dd HH:mm:ss.SSS}}{Dim}
%highlight{%5p} %style{%pid}{Magenta} %style{---}{Dim} %style{[%15.15t]}{Dim}
%style{%-40.40c}{Cyan} %style{:}{Dim} %m%n
-
-# log to console and file
-rootLogger = INFO,out,file
+# log to console
+rootLogger = INFO,out
rootLogger.level = INFO
-
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/LogConfigurationTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/LogConfigurationTest.java
new file mode 100644
index 000000000000..e391bb5aa0b6
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/LogConfigurationTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.common;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.appender.FileAppender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * CAMEL-25820: the logging configurations of the CLI write their files into
the Camel directory of the CLI, and the
+ * configuration that log4j finds on the classpath logs to the console only.
Each configuration is loaded into a logger
+ * context of its own, so the logging of the tests stays as it is, and no file
is created (the files are created on
+ * demand).
+ */
+class LogConfigurationTest {
+
+ private static final String PID =
Long.toString(ProcessHandle.current().pid());
+
+ @TempDir
+ Path dir;
+
+ @BeforeEach
+ void pid() {
+ // as the CLI sets it
+ System.setProperty("pid", PID);
+ }
+
+ @AfterEach
+ void clear() {
+ System.clearProperty(RuntimeUtil.LOG_DIR);
+ }
+
+ @ParameterizedTest
+ @CsvSource({
+ "log4j2-color.properties, PID.log",
+ "log4j2-no-color.properties, PID.log",
+ "log4j2-export.properties, camel-export.log",
+ "log4j2-script.properties, camel-script.log" })
+ void theLogFileIsInTheCamelDirectoryOfTheCli(String config, String file)
throws Exception {
+ System.setProperty(RuntimeUtil.LOG_DIR, dir.toString());
+
+ assertThat(fileName(config)).isEqualTo(dir.resolve(file.replace("PID",
PID)).toString());
+ }
+
+ @Test
+ void withoutTheDirectoryOfTheCliTheLogFileIsInTheCamelDirectoryOfTheUser()
throws Exception {
+ assertThat(fileName("log4j2-color.properties"))
+ .isEqualTo(Path.of(System.getProperty("user.home"), ".camel",
PID + ".log").toString());
+ }
+
+ @Test
+ void theConfigurationOnTheClasspathLogsToTheConsoleOnly() throws Exception
{
+ // the one of camel-jbang-core, not the one of these tests, which
comes first on the classpath of the tests
+ Path file = Path.of("src/main/resources/log4j2.properties");
+ Configuration config = load(new
ConfigurationSource(Files.newInputStream(file), file.toFile()));
+ try {
+ assertThat(config.getAppenders().values()).isNotEmpty()
+ .allSatisfy(a ->
assertThat(a).isInstanceOf(ConsoleAppender.class));
+ } finally {
+ config.stop();
+ }
+ }
+
+ // the name of the log file of the configuration, as log4j resolves it
+ private static String fileName(String resource) throws Exception {
+ Configuration config = load(ConfigurationSource.fromResource(resource,
LogConfigurationTest.class.getClassLoader()));
+ try {
+ Appender file = config.getAppender("file");
+ assertThat(file).isInstanceOf(FileAppender.class);
+ return ((FileAppender) file).getFileName();
+ } finally {
+ config.stop();
+ }
+ }
+
+ private static Configuration load(ConfigurationSource source) {
+ LoggerContext context = new LoggerContext("LogConfigurationTest");
+ Configuration config =
ConfigurationFactory.getInstance().getConfiguration(context, source);
+ config.initialize();
+ return config;
+ }
+}