|
15 | 15 | import org.slf4j.LoggerFactory; |
16 | 16 | import org.slf4j.profiler.Profiler; |
17 | 17 | import org.testcontainers.DockerClientFactory; |
| 18 | +import org.testcontainers.containers.output.OutputFrame; |
18 | 19 | import org.testcontainers.containers.output.Slf4jLogConsumer; |
19 | 20 | import org.testcontainers.containers.startupcheck.IndefiniteWaitOneShotStartupCheckStrategy; |
20 | 21 | import org.testcontainers.containers.wait.strategy.Wait; |
|
31 | 32 | import java.nio.file.Paths; |
32 | 33 | import java.time.Duration; |
33 | 34 | import java.util.AbstractMap.SimpleEntry; |
| 35 | +import java.util.ArrayList; |
34 | 36 | import java.util.Arrays; |
| 37 | +import java.util.Collections; |
35 | 38 | import java.util.HashMap; |
36 | 39 | import java.util.HashSet; |
37 | 40 | import java.util.List; |
|
40 | 43 | import java.util.concurrent.ConcurrentHashMap; |
41 | 44 | import java.util.concurrent.TimeUnit; |
42 | 45 | import java.util.concurrent.atomic.AtomicInteger; |
| 46 | +import java.util.function.Consumer; |
43 | 47 |
|
44 | 48 | import static com.google.common.base.Preconditions.checkArgument; |
45 | 49 | import static com.google.common.base.Preconditions.checkNotNull; |
@@ -72,6 +76,7 @@ public class DockerComposeContainer<SELF extends DockerComposeContainer<SELF>> e |
72 | 76 | private final Map<String, ComposeServiceWaitStrategyTarget> serviceInstanceMap = new ConcurrentHashMap<>(); |
73 | 77 | private final Map<String, WaitAllStrategy> waitStrategyMap = new ConcurrentHashMap<>(); |
74 | 78 | private final SocatContainer ambassadorContainer = new SocatContainer(); |
| 79 | + private final Map<String, List<Consumer<OutputFrame>>> logConsumers = new ConcurrentHashMap<>(); |
75 | 80 |
|
76 | 81 | private static final Object MUTEX = new Object(); |
77 | 82 |
|
@@ -148,11 +153,13 @@ private void createServiceInstance(Container container) { |
148 | 153 | final ComposeServiceWaitStrategyTarget containerInstance = new ComposeServiceWaitStrategyTarget(container, |
149 | 154 | ambassadorContainer, ambassadorPortMappings.getOrDefault(serviceName, new HashMap<>())); |
150 | 155 |
|
| 156 | + String containerId = containerInstance.getContainerId(); |
151 | 157 | if (tailChildContainers) { |
152 | | - LogUtils.followOutput(DockerClientFactory.instance().client(), containerInstance.getContainerId(), |
153 | | - new Slf4jLogConsumer(logger()).withPrefix(container.getNames()[0])); |
| 158 | + followLogs(containerId, new Slf4jLogConsumer(logger()).withPrefix(container.getNames()[0])); |
154 | 159 | } |
155 | | - serviceInstanceMap.putIfAbsent(serviceName, containerInstance); |
| 160 | + //follow logs using registered consumers for this service |
| 161 | + logConsumers.getOrDefault(serviceName, Collections.emptyList()).forEach(consumer -> followLogs(containerId, consumer)); |
| 162 | + serviceInstanceMap.putIfAbsent(serviceName, containerInstance); |
156 | 163 | } |
157 | 164 |
|
158 | 165 | private void waitUntilServiceStarted(String serviceName, ComposeServiceWaitStrategyTarget serviceInstance) { |
@@ -401,6 +408,27 @@ public SELF withTailChildContainers(boolean tailChildContainers) { |
401 | 408 | return self(); |
402 | 409 | } |
403 | 410 |
|
| 411 | + /** |
| 412 | + * Attach an output consumer at container startup, enabling stdout and stderr to be followed, waited on, etc. |
| 413 | + * <p> |
| 414 | + * More than one consumer may be registered. |
| 415 | + * |
| 416 | + * @param serviceName the name of the service as set in the docker-compose.yml file |
| 417 | + * @param consumer consumer that output frames should be sent to |
| 418 | + * @return this instance, for chaining |
| 419 | + */ |
| 420 | + public SELF withLogConsumer(String serviceName, Consumer<OutputFrame> consumer) { |
| 421 | + String serviceInstanceName = getServiceInstanceName(serviceName); |
| 422 | + final List<Consumer<OutputFrame>> consumers = this.logConsumers.getOrDefault(serviceInstanceName, new ArrayList<>()); |
| 423 | + consumers.add(consumer); |
| 424 | + this.logConsumers.putIfAbsent(serviceInstanceName, consumers); |
| 425 | + return self(); |
| 426 | + } |
| 427 | + |
| 428 | + private void followLogs(String containerId, Consumer<OutputFrame> consumer) { |
| 429 | + LogUtils.followOutput(DockerClientFactory.instance().client(), containerId, consumer); |
| 430 | + } |
| 431 | + |
404 | 432 | private SELF self() { |
405 | 433 | return (SELF) this; |
406 | 434 | } |
|
0 commit comments