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;
+    }
+}

Reply via email to