Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.testcontainers.utility.DockerLoggerFactory;
import org.testcontainers.utility.DockerMachineClient;
import org.testcontainers.utility.DynamicPollInterval;
import org.testcontainers.utility.LogUtils;
import org.testcontainers.utility.MountableFile;
import org.testcontainers.utility.PathUtils;
import org.testcontainers.utility.ResourceReaper;
Expand Down Expand Up @@ -135,6 +136,15 @@ public class GenericContainer<SELF extends GenericContainer<SELF>>

private int startupAttempts = 1;

/**
* Log output captured from the container when it failed to start. Kept so that callers can still
* inspect it after the failed container has been removed.
*/
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
@Nullable
private String logsFromFailedStartup = null;

@Nullable
private String workingDirectory = null;

Expand Down Expand Up @@ -302,6 +312,20 @@ public String getContainerId() {
return containerId;
}

/**
* {@inheritDoc}
* <p>
* If the container failed to start it will already have been removed, so the log output that was
* captured during the failure is returned instead.
*/
@Override
public String getLogs() {
if (getContainerId() == null) {
return this.logsFromFailedStartup != null ? this.logsFromFailedStartup : "";
}
return LogUtils.getOutput(getDockerClient(), getContainerId());
}

/**
* Starts the container using docker, pulling an image if necessary.
*/
Expand Down Expand Up @@ -539,6 +563,8 @@ private void tryStart() {
if (containerId != null) {
// Log output if startup failed, either due to a container failure or exception (including timeout)
final String containerLogs = getLogs();
// Keep the logs around: stop() below removes the container, making them unreachable afterwards
this.logsFromFailedStartup = containerLogs;

if (containerLogs.length() > 0) {
logger().error("Log output from the failed container:\n{}", containerLogs);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.testcontainers.containers;

import org.junit.jupiter.api.Test;
import org.testcontainers.TestImages;
import org.testcontainers.containers.wait.strategy.Wait;

import java.time.Duration;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class LogsAfterStartupFailureTest {

@Test
void shouldKeepLogsAfterStartupFailure() {
try (
GenericContainer<?> container = new GenericContainer<>(TestImages.TINY_IMAGE)
.withCommand("sh", "-c", "echo 'something went wrong'; exit 1")
.waitingFor(
Wait.forLogMessage(".*this will never appear.*", 1).withStartupTimeout(Duration.ofSeconds(10))
)
) {
assertThatThrownBy(container::start).isInstanceOf(ContainerLaunchException.class);

assertThat(container.getLogs())
.as("log output of the failed container is still available after startup failed")
.contains("something went wrong");
}
}
}