or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

containers.mddocker-compose.mdimages-files.mdindex.mdjunit-integration.mdnetworks.mdoutput-logging.mdtestcontainers-utility.mdwait-strategies.md

output-logging.mddocs/

0

# Output and Logging

1

2

Container output handling and log consumption capabilities for monitoring container behavior, debugging test failures, and processing container output streams in real-time.

3

4

## Capabilities

5

6

### Output Frame

7

8

Representation of container output with support for different output types and content processing.

9

10

```java { .api }

11

/**

12

* Represents a frame of output from a container

13

*/

14

public class OutputFrame {

15

16

/** Output types from container streams */

17

public enum OutputType {

18

/** Standard output stream */

19

STDOUT,

20

/** Standard error stream */

21

STDERR,

22

/** End of output marker */

23

END

24

}

25

26

/** Get output content as UTF-8 string */

27

public String getUtf8String();

28

29

/** Get output content as raw bytes */

30

public byte[] getBytes();

31

32

/** Get the type of output (stdout/stderr/end) */

33

public OutputType getType();

34

35

/** Get timestamp when output was received */

36

public Instant getTimestamp();

37

38

/** Check if this is stdout output */

39

public boolean isStdout();

40

41

/** Check if this is stderr output */

42

public boolean isStderr();

43

44

/** Check if this is end marker */

45

public boolean isEnd();

46

47

@Override

48

public String toString();

49

}

50

```

51

52

### Log Consumers

53

54

Various implementations for consuming and processing container output streams.

55

56

```java { .api }

57

/**

58

* SLF4J integration for container logs

59

*/

60

public class Slf4jLogConsumer implements Consumer<OutputFrame> {

61

62

/** Create with specific logger */

63

public Slf4jLogConsumer(Logger logger);

64

65

/** Create with logger for specific class */

66

public Slf4jLogConsumer(Class<?> clazz);

67

68

/** Create with logger name */

69

public Slf4jLogConsumer(String loggerName);

70

71

/** Configure to separate stdout/stderr to different log levels */

72

public Slf4jLogConsumer withSeparateOutputStreams();

73

74

/** Configure prefix for log messages */

75

public Slf4jLogConsumer withPrefix(String prefix);

76

77

/** Configure to remove ANSI escape sequences */

78

public Slf4jLogConsumer withRemoveAnsiCodes(boolean removeAnsiCodes);

79

80

@Override

81

public void accept(OutputFrame outputFrame);

82

}

83

84

/**

85

* Consumer that collects output into a string buffer

86

*/

87

public class ToStringConsumer implements Consumer<OutputFrame> {

88

89

public ToStringConsumer();

90

91

/** Get all collected output as string */

92

public String toUtf8String();

93

94

/** Get output of specific types only */

95

public String toUtf8String(OutputFrame.OutputType... types);

96

97

/** Clear collected output */

98

public void clear();

99

100

@Override

101

public void accept(OutputFrame outputFrame);

102

}

103

104

/**

105

* Consumer that waits for specific output patterns

106

*/

107

public class WaitingConsumer implements Consumer<OutputFrame> {

108

109

public WaitingConsumer();

110

111

/** Wait for specific predicate condition in output */

112

public void waitUntil(Predicate<OutputFrame> predicate) throws TimeoutException;

113

public void waitUntil(Predicate<OutputFrame> predicate, long timeout, TimeUnit timeUnit) throws TimeoutException;

114

115

/** Wait for output to end */

116

public void waitUntilEnd() throws TimeoutException;

117

public void waitUntilEnd(long timeout, TimeUnit timeUnit) throws TimeoutException;

118

119

/** Get all collected frames */

120

public List<OutputFrame> getFrames();

121

122

/** Get frames of specific types */

123

public List<OutputFrame> getFrames(OutputFrame.OutputType... types);

124

125

/** Convert collected output to string */

126

public String toUtf8String();

127

128

@Override

129

public void accept(OutputFrame outputFrame);

130

}

131

```

132

133

**Usage Examples:**

134

135

```java

136

import org.testcontainers.containers.output.OutputFrame;

137

import org.testcontainers.containers.output.Slf4jLogConsumer;

138

import org.testcontainers.containers.output.ToStringConsumer;

139

import org.testcontainers.containers.output.WaitingConsumer;

140

import org.slf4j.LoggerFactory;

141

142

// SLF4J logging integration

143

Logger logger = LoggerFactory.getLogger("container-logs");

144

Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(logger)

145

.withPrefix("APP")

146

.withSeparateOutputStreams()

147

.withRemoveAnsiCodes(true);

148

149

GenericContainer<?> container = new GenericContainer<>("myapp:latest")

150

.withLogConsumer(logConsumer)

151

.withExposedPorts(8080);

152

153

// Collect output to string

154

ToStringConsumer stringConsumer = new ToStringConsumer();

155

container.withLogConsumer(stringConsumer);

156

container.start();

157

158

String allLogs = stringConsumer.toUtf8String();

159

String errorLogs = stringConsumer.toUtf8String(OutputFrame.OutputType.STDERR);

160

161

// Wait for specific output

162

WaitingConsumer waitingConsumer = new WaitingConsumer();

163

container.withLogConsumer(waitingConsumer);

164

container.start();

165

166

// Wait for application to be ready

167

waitingConsumer.waitUntilString("Server started", 30, TimeUnit.SECONDS);

168

```

169

170

### Log Consumer Composition

171

172

Combining multiple log consumers to process output in different ways simultaneously.

173

174

```java { .api }

175

/**

176

* Utility for composing multiple log consumers

177

*/

178

public class ComposingLogConsumer implements Consumer<OutputFrame> {

179

180

public ComposingLogConsumer(Consumer<OutputFrame>... consumers);

181

public ComposingLogConsumer(List<Consumer<OutputFrame>> consumers);

182

183

/** Add additional consumer */

184

public ComposingLogConsumer withConsumer(Consumer<OutputFrame> consumer);

185

186

@Override

187

public void accept(OutputFrame outputFrame);

188

}

189

```

190

191

**Usage Examples:**

192

193

```java

194

// Combine multiple consumers

195

Consumer<OutputFrame> compositeConsumer = new ComposingLogConsumer(

196

new Slf4jLogConsumer(logger),

197

stringConsumer,

198

waitingConsumer

199

);

200

201

container.withLogConsumer(compositeConsumer);

202

```

203

204

### Container Log Access

205

206

Direct access to container logs through container state and methods.

207

208

```java { .api }

209

/**

210

* Container log access methods (from GenericContainer and ContainerState)

211

*/

212

// Get all logs as string

213

public String getLogs();

214

215

// Get logs of specific output types

216

public String getLogs(OutputFrame.OutputType... types);

217

218

// Get logs since specific timestamp

219

public String getLogs(Instant since);

220

221

// Get logs with specific options

222

public String getLogs(LogOptions options);

223

224

/**

225

* Options for log retrieval

226

*/

227

public static class LogOptions {

228

public static LogOptions defaultOptions();

229

230

public LogOptions withTypes(OutputFrame.OutputType... types);

231

public LogOptions withSince(Instant since);

232

public LogOptions withUntil(Instant until);

233

public LogOptions withTail(int lines);

234

public LogOptions withTimestamps(boolean timestamps);

235

}

236

```

237

238

**Usage Examples:**

239

240

```java

241

// Basic log access

242

String allLogs = container.getLogs();

243

String errorLogs = container.getLogs(OutputFrame.OutputType.STDERR);

244

245

// Advanced log access

246

String recentLogs = container.getLogs(

247

LogOptions.defaultOptions()

248

.withSince(Instant.now().minus(Duration.ofMinutes(5)))

249

.withTail(100)

250

.withTimestamps(true)

251

);

252

```

253

254

### Custom Log Consumers

255

256

Creating custom log consumers for specialized output processing.

257

258

```java { .api }

259

/**

260

* Base interface for implementing custom log consumers

261

*/

262

@FunctionalInterface

263

public interface Consumer<T> {

264

void accept(T t);

265

}

266

267

// Example custom consumer implementation

268

public class MetricsLogConsumer implements Consumer<OutputFrame> {

269

270

private final Map<String, AtomicInteger> metricCounts = new ConcurrentHashMap<>();

271

private final Pattern metricPattern = Pattern.compile("METRIC:\\s*(\\w+):(\\d+)");

272

273

@Override

274

public void accept(OutputFrame outputFrame) {

275

if (outputFrame.getType() == OutputFrame.OutputType.STDOUT) {

276

String content = outputFrame.getUtf8String();

277

Matcher matcher = metricPattern.matcher(content);

278

279

while (matcher.find()) {

280

String metricName = matcher.group(1);

281

int metricValue = Integer.parseInt(matcher.group(2));

282

metricCounts.computeIfAbsent(metricName, k -> new AtomicInteger(0))

283

.addAndGet(metricValue);

284

}

285

}

286

}

287

288

public Map<String, Integer> getMetrics() {

289

return metricCounts.entrySet().stream()

290

.collect(Collectors.toMap(

291

Map.Entry::getKey,

292

e -> e.getValue().get()

293

));

294

}

295

}

296

```

297

298

## Advanced Output Processing Patterns

299

300

### Real-Time Log Analysis

301

302

Processing container output in real-time for monitoring and alerting:

303

304

```java

305

@Test

306

public void testRealTimeLogMonitoring() {

307

AtomicBoolean errorDetected = new AtomicBoolean(false);

308

CountDownLatch errorLatch = new CountDownLatch(1);

309

310

Consumer<OutputFrame> errorDetector = outputFrame -> {

311

if (outputFrame.getType() == OutputFrame.OutputType.STDERR) {

312

String content = outputFrame.getUtf8String();

313

if (content.contains("FATAL") || content.contains("ERROR")) {

314

errorDetected.set(true);

315

errorLatch.countDown();

316

}

317

}

318

};

319

320

GenericContainer<?> container = new GenericContainer<>("problematic-app:latest")

321

.withLogConsumer(errorDetector)

322

.withExposedPorts(8080);

323

324

container.start();

325

326

// Wait for error detection or timeout

327

boolean errorFound = errorLatch.await(30, TimeUnit.SECONDS);

328

329

if (errorFound) {

330

fail("Application reported fatal error: " + container.getLogs(OutputFrame.OutputType.STDERR));

331

}

332

}

333

```

334

335

### Structured Log Processing

336

337

Processing structured logs (JSON, key-value pairs) from containers:

338

339

```java

340

public class JsonLogConsumer implements Consumer<OutputFrame> {

341

342

private final ObjectMapper objectMapper = new ObjectMapper();

343

private final List<Map<String, Object>> logEntries = new ArrayList<>();

344

345

@Override

346

public void accept(OutputFrame outputFrame) {

347

if (outputFrame.getType() == OutputFrame.OutputType.STDOUT) {

348

String content = outputFrame.getUtf8String().trim();

349

350

if (content.startsWith("{") && content.endsWith("}")) {

351

try {

352

Map<String, Object> logEntry = objectMapper.readValue(content, Map.class);

353

synchronized (logEntries) {

354

logEntries.add(logEntry);

355

}

356

} catch (JsonProcessingException e) {

357

// Not valid JSON, ignore

358

}

359

}

360

}

361

}

362

363

public List<Map<String, Object>> getLogEntries() {

364

synchronized (logEntries) {

365

return new ArrayList<>(logEntries);

366

}

367

}

368

369

public List<Map<String, Object>> findLogEntries(String level) {

370

return getLogEntries().stream()

371

.filter(entry -> level.equals(entry.get("level")))

372

.collect(Collectors.toList());

373

}

374

}

375

```

376

377

### Performance Monitoring

378

379

Monitoring application performance through log output:

380

381

```java

382

public class PerformanceLogConsumer implements Consumer<OutputFrame> {

383

384

private final Pattern responseTimePattern = Pattern.compile("Response time: (\\d+)ms");

385

private final List<Long> responseTimes = new ArrayList<>();

386

387

@Override

388

public void accept(OutputFrame outputFrame) {

389

String content = outputFrame.getUtf8String();

390

Matcher matcher = responseTimePattern.matcher(content);

391

392

while (matcher.find()) {

393

long responseTime = Long.parseLong(matcher.group(1));

394

synchronized (responseTimes) {

395

responseTimes.add(responseTime);

396

}

397

}

398

}

399

400

public double getAverageResponseTime() {

401

synchronized (responseTimes) {

402

return responseTimes.stream()

403

.mapToLong(Long::longValue)

404

.average()

405

.orElse(0.0);

406

}

407

}

408

409

public long getMaxResponseTime() {

410

synchronized (responseTimes) {

411

return responseTimes.stream()

412

.mapToLong(Long::longValue)

413

.max()

414

.orElse(0L);

415

}

416

}

417

}

418

```

419

420

### Log Filtering and Transformation

421

422

Filtering and transforming log output before processing:

423

424

```java

425

public class FilteringLogConsumer implements Consumer<OutputFrame> {

426

427

private final Consumer<OutputFrame> delegate;

428

private final Predicate<OutputFrame> filter;

429

private final Function<OutputFrame, OutputFrame> transformer;

430

431

public FilteringLogConsumer(Consumer<OutputFrame> delegate,

432

Predicate<OutputFrame> filter,

433

Function<OutputFrame, OutputFrame> transformer) {

434

this.delegate = delegate;

435

this.filter = filter;

436

this.transformer = transformer;

437

}

438

439

@Override

440

public void accept(OutputFrame outputFrame) {

441

if (filter.test(outputFrame)) {

442

OutputFrame transformed = transformer.apply(outputFrame);

443

delegate.accept(transformed);

444

}

445

}

446

447

// Factory methods for common filters

448

public static FilteringLogConsumer onlyErrors(Consumer<OutputFrame> delegate) {

449

return new FilteringLogConsumer(

450

delegate,

451

frame -> frame.getType() == OutputFrame.OutputType.STDERR,

452

Function.identity()

453

);

454

}

455

456

public static FilteringLogConsumer withoutTimestamps(Consumer<OutputFrame> delegate) {

457

return new FilteringLogConsumer(

458

delegate,

459

frame -> true,

460

frame -> {

461

String content = frame.getUtf8String();

462

// Remove timestamp prefix pattern

463

String cleaned = content.replaceFirst("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z\\s+", "");

464

return new OutputFrame(frame.getType(), cleaned.getBytes(StandardCharsets.UTF_8));

465

}

466

);

467

}

468

}

469

```

470

471

### Asynchronous Log Processing

472

473

Processing logs asynchronously to avoid blocking container operations:

474

475

```java

476

public class AsyncLogConsumer implements Consumer<OutputFrame>, AutoCloseable {

477

478

private final Consumer<OutputFrame> delegate;

479

private final ExecutorService executor;

480

private final BlockingQueue<OutputFrame> queue;

481

private final AtomicBoolean running = new AtomicBoolean(true);

482

483

public AsyncLogConsumer(Consumer<OutputFrame> delegate) {

484

this.delegate = delegate;

485

this.executor = Executors.newSingleThreadExecutor();

486

this.queue = new LinkedBlockingQueue<>();

487

488

// Start background processing thread

489

executor.submit(this::processLogs);

490

}

491

492

@Override

493

public void accept(OutputFrame outputFrame) {

494

if (running.get()) {

495

queue.offer(outputFrame);

496

}

497

}

498

499

private void processLogs() {

500

while (running.get() || !queue.isEmpty()) {

501

try {

502

OutputFrame frame = queue.poll(100, TimeUnit.MILLISECONDS);

503

if (frame != null) {

504

delegate.accept(frame);

505

}

506

} catch (InterruptedException e) {

507

Thread.currentThread().interrupt();

508

break;

509

}

510

}

511

}

512

513

@Override

514

public void close() {

515

running.set(false);

516

executor.shutdown();

517

try {

518

if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {

519

executor.shutdownNow();

520

}

521

} catch (InterruptedException e) {

522

executor.shutdownNow();

523

Thread.currentThread().interrupt();

524

}

525

}

526

}