Skip to content

Commit a75824c

Browse files
Add withLogConsumer + general logging improvements (#559)
1 parent 0af4d35 commit a75824c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+311
-224
lines changed

docs/configuration.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ All possible environment variable configurations for Testcontainers are found he
99
| DEBUG | testcontainers* | Enable all logs |
1010
| DEBUG | testcontainers | Enable testcontainers logs |
1111
| DEBUG | testcontainers:containers | Enable container logs |
12+
| DEBUG | testcontainers:compose | Enable compose logs |
13+
| DEBUG | testcontainers:build | Enable build logs |
14+
| DEBUG | testcontainers:pull | Enable pull logs |
1215
| DEBUG | testcontainers:exec | Enable container exec logs |
1316

1417
Note that you can enable multiple loggers, e.g: `DEBUG=testcontainers,testcontainers:exec`.

docs/features/containers.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,8 @@ const { output, exitCode } = await container.exec(["echo", "hello", "world"]);
410410

411411
## Streaming logs
412412

413+
Logs can be consumed either from a started container:
414+
413415
```javascript
414416
const container = await new GenericContainer("alpine").start();
415417

@@ -418,3 +420,15 @@ const container = await new GenericContainer("alpine").start();
418420
.on("err", line => console.error(line))
419421
.on("end", () => console.log("Stream closed"));
420422
```
423+
424+
Or a consumer can be provided before start. This is useful for example if your container is failing to start:
425+
426+
```javascript
427+
const container = await new GenericContainer("alpine")
428+
.withLogConsumer(stream => {
429+
stream.on("data", line => console.log(line));
430+
stream.on("err", line => console.error(line));
431+
stream.on("end", () => console.log("Stream closed"));
432+
})
433+
.start();
434+
```

src/docker-compose-environment/docker-compose-environment.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export class DockerComposeEnvironment {
8282
}
8383

8484
public async up(services?: Array<string>): Promise<StartedDockerComposeEnvironment> {
85-
log.info(`Starting DockerCompose environment: ${this.projectName}`);
85+
log.info(`Starting DockerCompose environment "${this.projectName}"...`);
8686

8787
(await ReaperInstance.getInstance()).addProject(this.projectName);
8888

@@ -123,7 +123,7 @@ export class DockerComposeEnvironment {
123123
[]
124124
);
125125

126-
log.info(`Started the following containers: ${startedContainerNames.join(", ")}`);
126+
log.info(`Started containers "${startedContainerNames.join('", "')}"`);
127127

128128
const startedGenericContainers = (
129129
await Promise.all(
@@ -148,11 +148,8 @@ export class DockerComposeEnvironment {
148148
}
149149

150150
try {
151-
log.info(`Waiting for container ${containerName} to be ready`);
152151
await waitForContainer(container, waitStrategy, boundPorts);
153-
log.info(`Container ${containerName} is ready`);
154152
} catch (err) {
155-
log.error(`Container ${containerName} failed to be ready: ${err}`);
156153
try {
157154
await dockerComposeDown(options, { removeVolumes: true, timeout: 0 });
158155
} catch {
@@ -169,7 +166,7 @@ export class DockerComposeEnvironment {
169166
return { ...map, [containerName]: startedGenericContainer };
170167
}, {});
171168

172-
log.info(`DockerCompose environment started: ${Object.keys(startedGenericContainers).join(", ")}`);
169+
log.info(`DockerCompose environment started`);
173170

174171
return new StartedDockerComposeEnvironment(startedGenericContainers, {
175172
...options,

src/docker-compose/default-docker-compose-options.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { IDockerComposeOptions } from "docker-compose";
22
import { DockerComposeOptions } from "./docker-compose-options";
33
import { dockerClient } from "../docker/docker-client";
4-
import { log } from "../logger";
4+
import { composeLog } from "../logger";
55
import { EOL } from "os";
66
import { isNotEmptyString } from "../type-guards";
77

@@ -10,6 +10,7 @@ export const defaultDockerComposeOptions = async ({
1010
...options
1111
}: DockerComposeOptions): Promise<Partial<IDockerComposeOptions>> => {
1212
const { composeEnvironment } = await dockerClient();
13+
const log = options.logger ?? composeLog;
1314

1415
return {
1516
log: false,

src/docker-compose/docker-compose-options.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Environment } from "../docker/types";
2+
import { Logger } from "../logger";
23

34
export type DockerComposeOptions = {
45
filePath: string;
@@ -7,4 +8,5 @@ export type DockerComposeOptions = {
78
commandOptions?: string[];
89
composeOptions?: string[];
910
environment?: Environment;
11+
logger?: Logger;
1012
};

src/docker-compose/functions/docker-compose-down.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export const dockerComposeDown = async (
88
options: DockerComposeOptions,
99
downOptions: DockerComposeDownOptions
1010
): Promise<void> => {
11-
log.info(`Downing DockerCompose environment`);
11+
log.info(`Downing DockerCompose environment...`);
1212

1313
try {
1414
await down({ ...(await defaultDockerComposeOptions(options)), commandOptions: commandOptions(downOptions) });

src/docker-compose/functions/docker-compose-pull.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import { log } from "../../logger";
1+
import { pullLog, log } from "../../logger";
22
import { pullAll, pullMany } from "docker-compose";
33
import { defaultDockerComposeOptions } from "../default-docker-compose-options";
44
import { DockerComposeOptions } from "../docker-compose-options";
55

66
export const dockerComposePull = async (options: DockerComposeOptions, services?: Array<string>): Promise<void> => {
77
try {
88
if (services) {
9-
log.info(`Pulling DockerCompose environment images for ${services.join(", ")}`);
10-
await pullMany(services, await defaultDockerComposeOptions(options));
9+
log.info(`Pulling DockerCompose environment images "${services.join('", "')}"...`);
10+
await pullMany(services, await defaultDockerComposeOptions({ ...options, logger: pullLog }));
1111
} else {
12-
log.info(`Pulling DockerCompose environment images`);
13-
await pullAll(await defaultDockerComposeOptions(options));
12+
log.info(`Pulling DockerCompose environment images...`);
13+
await pullAll(await defaultDockerComposeOptions({ ...options, logger: pullLog }));
1414
}
1515
log.info(`Pulled DockerCompose environment`);
1616
// eslint-disable-next-line @typescript-eslint/no-explicit-any

src/docker-compose/functions/docker-compose-stop.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { defaultDockerComposeOptions } from "../default-docker-compose-options";
44
import { DockerComposeOptions } from "../docker-compose-options";
55

66
export const dockerComposeStop = async (options: DockerComposeOptions): Promise<void> => {
7-
log.info(`Stopping DockerCompose environment`);
7+
log.info(`Stopping DockerCompose environment...`);
88

99
try {
1010
await stop(await defaultDockerComposeOptions(options));

src/docker-compose/functions/docker-compose-up.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { DockerComposeOptions } from "../docker-compose-options";
55
import { dockerComposeDown } from "./docker-compose-down";
66

77
export const dockerComposeUp = async (options: DockerComposeOptions, services?: Array<string>): Promise<void> => {
8-
log.info(`Upping DockerCompose environment`);
8+
log.info(`Upping DockerCompose environment...`);
99

1010
try {
1111
if (services) {

src/docker/docker-client-config.ts

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,53 +12,65 @@ export type DockerClientConfig = {
1212
};
1313

1414
export const getDockerClientConfig = async (env: NodeJS.ProcessEnv = process.env): Promise<DockerClientConfig> => {
15-
let dockerHost: string | undefined;
16-
let dockerTlsVerify: string | undefined;
17-
let dockerCertPath: string | undefined;
15+
const dockerClientConfig: DockerClientConfig = {
16+
...(await loadFromFile()),
17+
...loadFromEnv(env),
18+
};
19+
logDockerClientConfig(dockerClientConfig);
20+
return dockerClientConfig;
21+
};
1822

23+
async function loadFromFile() {
1924
const file = path.resolve(homedir(), ".testcontainers.properties");
25+
26+
const dockerClientConfig: DockerClientConfig = {};
27+
2028
if (existsSync(file)) {
21-
log.debug("Found .testcontainers.properties file");
29+
log.debug(`Found ".testcontainers.properties" file`);
2230
const string = await readFile(file, { encoding: "utf-8" });
2331
const properties = propertiesReader("").read(string);
2432

2533
const host = properties.get("docker.host") as string;
2634
if (host !== null) {
27-
dockerHost = host;
35+
dockerClientConfig.dockerHost = host;
2836
}
2937

3038
const tlsVerify = properties.get("docker.tls.verify") as number;
3139
if (tlsVerify !== null) {
32-
dockerTlsVerify = `${tlsVerify}`;
40+
dockerClientConfig.dockerTlsVerify = `${tlsVerify}`;
3341
}
3442

3543
const certPath = properties.get("docker.cert.path") as string;
3644
if (certPath !== null) {
37-
dockerCertPath = certPath;
45+
dockerClientConfig.dockerCertPath = certPath;
3846
}
3947
}
4048

49+
return dockerClientConfig;
50+
}
51+
52+
function loadFromEnv(env: NodeJS.ProcessEnv) {
53+
const dockerClientConfig: DockerClientConfig = {};
54+
4155
if (env["DOCKER_HOST"] !== undefined) {
42-
dockerHost = env["DOCKER_HOST"];
56+
dockerClientConfig.dockerHost = env["DOCKER_HOST"];
4357
}
4458
if (env["DOCKER_TLS_VERIFY"] !== undefined) {
45-
dockerTlsVerify = env["DOCKER_TLS_VERIFY"];
59+
dockerClientConfig.dockerTlsVerify = env["DOCKER_TLS_VERIFY"];
4660
}
4761
if (env["DOCKER_CERT_PATH"] !== undefined) {
48-
dockerCertPath = env["DOCKER_CERT_PATH"];
62+
dockerClientConfig.dockerCertPath = env["DOCKER_CERT_PATH"];
4963
}
5064

51-
let logStr = "Loaded properties: ";
52-
if (dockerHost !== undefined) {
53-
logStr += `dockerHost=${dockerHost}, `;
54-
}
55-
if (dockerTlsVerify !== undefined) {
56-
logStr += `dockerTlsVerify=${dockerTlsVerify}, `;
57-
}
58-
if (dockerCertPath !== undefined) {
59-
logStr += `dockerCertPath=${dockerCertPath}`;
60-
}
61-
log.debug(logStr);
65+
return dockerClientConfig;
66+
}
6267

63-
return { dockerHost, dockerTlsVerify, dockerCertPath };
64-
};
68+
function logDockerClientConfig(config: DockerClientConfig) {
69+
const configurations = Object.entries(config)
70+
.filter(([, value]) => value !== undefined)
71+
.map(([key, value]) => `${key}: "${value}"`);
72+
73+
if (configurations.length > 0) {
74+
log.debug(`Loaded Docker client configuration, ${configurations.join(", ")}`);
75+
}
76+
}

0 commit comments

Comments
 (0)