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 fb0b9fbfc835 CAMEL-25732: camel-docker - log the throwable at DEBUG
when the events stream fails before it opens (#29439)
fb0b9fbfc835 is described below
commit fb0b9fbfc835dfee776a440d1e222d11b9e83b22
Author: allthingssecurity <[email protected]>
AuthorDate: Mon Nov 9 15:04:59 2026 +0530
CAMEL-25732: camel-docker - log the throwable at DEBUG when the events
stream fails before it opens (#29439)
* CAMEL-25732: camel-docker - log the throwable at DEBUG when the events
stream fails before it opens
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../docker/consumer/DockerEventsConsumer.java | 2 +
.../consumer/DockerEventsConsumerStreamTest.java | 68 ++++++++++++++++++++++
2 files changed, 70 insertions(+)
diff --git
a/components/camel-docker/src/main/java/org/apache/camel/component/docker/consumer/DockerEventsConsumer.java
b/components/camel-docker/src/main/java/org/apache/camel/component/docker/consumer/DockerEventsConsumer.java
index b0c7917c3476..ae080e01dbbd 100644
---
a/components/camel-docker/src/main/java/org/apache/camel/component/docker/consumer/DockerEventsConsumer.java
+++
b/components/camel-docker/src/main/java/org/apache/camel/component/docker/consumer/DockerEventsConsumer.java
@@ -239,8 +239,10 @@ public class DockerEventsConsumer extends DefaultConsumer {
LOG.warn("The Docker event stream of {} failed, subscribing
again in {} ms: {}", endpoint, delay,
throwable.getMessage(), throwable);
} else {
+ // repeats for every attempt while the daemon is down, so one
line, and the stack trace at DEBUG
LOG.warn("Failed to subscribe to the Docker events of {},
trying again in {} ms: {}", endpoint, delay,
throwable.getMessage());
+ LOG.debug("Failed to subscribe to the Docker events of {}",
endpoint, throwable);
}
subscribeAgain(delay);
}
diff --git
a/components/camel-docker/src/test/java/org/apache/camel/component/docker/consumer/DockerEventsConsumerStreamTest.java
b/components/camel-docker/src/test/java/org/apache/camel/component/docker/consumer/DockerEventsConsumerStreamTest.java
index 6a8691e34935..875327a150f2 100644
---
a/components/camel-docker/src/test/java/org/apache/camel/component/docker/consumer/DockerEventsConsumerStreamTest.java
+++
b/components/camel-docker/src/test/java/org/apache/camel/component/docker/consumer/DockerEventsConsumerStreamTest.java
@@ -22,6 +22,7 @@ import java.net.ConnectException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import com.github.dockerjava.api.DockerClient;
@@ -37,6 +38,12 @@ import org.apache.camel.component.docker.DockerConfiguration;
import org.apache.camel.component.docker.util.DockerTestUtils;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.junit6.CamelTestSupport;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.config.Property;
import org.junit.jupiter.api.Test;
import static org.awaitility.Awaitility.await;
@@ -55,6 +62,8 @@ class DockerEventsConsumerStreamTest extends CamelTestSupport
{
private final DockerClient dockerClient = mock(DockerClient.class);
private final List<ResultCallback<Event>> eventCallbacks = new
CopyOnWriteArrayList<>();
private final List<ResultCallback<Statistics>> statsCallbacks = new
CopyOnWriteArrayList<>();
+ private final AtomicInteger eventExecs = new AtomicInteger();
+ private final AtomicBoolean failEventExec = new AtomicBoolean();
@Override
public boolean isUseRouteBuilder() {
@@ -75,6 +84,12 @@ class DockerEventsConsumerStreamTest extends
CamelTestSupport {
EventsCmd cmd = mock(EventsCmd.class);
when(cmd.withSince(anyString())).thenReturn(cmd);
when(cmd.exec(any())).thenAnswer(e -> {
+ eventExecs.incrementAndGet();
+ if (failEventExec.getAndSet(false)) {
+ // what the default Netty transport does when the daemon
cannot be reached: the connect fails
+ // synchronously, and the callback is not called
+ throw new ConnectException("Connection refused");
+ }
ResultCallback<Event> callback = e.getArgument(0);
eventCallbacks.add(callback);
return callback;
@@ -148,6 +163,59 @@ class DockerEventsConsumerStreamTest extends
CamelTestSupport {
mock.assertIsSatisfied();
}
+ @Test
+ void failingExecWhenSubscribingAgainIsTriedAgain() throws Exception {
+ addRoute("docker://events?host=localhost&port=2375");
+ eventCallbacks.get(0).onStart(() -> {
+ });
+
+ // the daemon goes away: the stream fails, and the first attempt to
subscribe again cannot connect
+ failEventExec.set(true);
+ eventCallbacks.get(0).onError(new IOException("Connection reset by
peer"));
+
+ // the consumer keeps subscribing: the next attempt opens a new stream
+ await().atMost(15, TimeUnit.SECONDS).until(() -> eventCallbacks.size()
== 2);
+ assertEquals(3, eventExecs.get(), "the start, the attempt that failed
and the one that succeeded");
+
+ MockEndpoint mock = getMockEndpoint("mock:result");
+ mock.expectedBodiesReceived(new Event("start", "c2", "image", 2L));
+ eventCallbacks.get(1).onStart(() -> {
+ });
+ eventCallbacks.get(1).onNext(new Event("start", "c2", "image", 2L));
+ mock.assertIsSatisfied();
+ }
+
+ @Test
+ void streamThatFailsBeforeItIsOpenLogsTheStackTraceAtDebug() throws
Exception {
+ addRoute("docker://events?host=localhost&port=2375");
+ List<LogEvent> events = new CopyOnWriteArrayList<>();
+ AbstractAppender appender = new AbstractAppender("subscribeFailures",
null, null, true, Property.EMPTY_ARRAY) {
+ @Override
+ public void append(LogEvent event) {
+ events.add(event.toImmutable());
+ }
+ };
+ appender.start();
+ Logger logger = (Logger)
LogManager.getLogger(DockerEventsConsumer.class);
+ Level level = logger.getLevel();
+ logger.addAppender(appender);
+ logger.setLevel(Level.DEBUG);
+ ConnectException refused = new ConnectException("Connection refused");
+ try {
+ // the stream fails before it is open (the daemon cannot be
reached)
+ eventCallbacks.get(0).onError(refused);
+ } finally {
+ logger.setLevel(level);
+ logger.removeAppender(appender);
+ appender.stop();
+ }
+
+ // one WARN line without the stack trace, and the stack trace at DEBUG
+ assertEquals(1, events.stream().filter(e -> e.getLevel() == Level.WARN
&& e.getThrown() == null).count());
+ assertEquals(0, events.stream().filter(e -> e.getLevel() == Level.WARN
&& e.getThrown() != null).count());
+ assertEquals(1, events.stream().filter(e -> e.getLevel() ==
Level.DEBUG && e.getThrown() == refused).count());
+ }
+
@Test
void eventStreamThatEndsIsOpenedAgain() throws Exception {
addRoute("docker://events?host=localhost&port=2375");