Skip to content

Commit 413c026

Browse files
committed
capture zenith logs on test failure
1 parent 908b24a commit 413c026

5 files changed

Lines changed: 131 additions & 16 deletions

File tree

src/test/java/com/zenith/ConnectIntegTest.java

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@
33
import com.zenith.util.Wait;
44
import com.zenith.util.config.Config;
55
import org.junit.jupiter.api.Test;
6+
import org.junit.jupiter.api.extension.ExtendWith;
67
import org.testcontainers.containers.GenericContainer;
78
import org.testcontainers.junit.jupiter.Container;
89
import org.testcontainers.junit.jupiter.Testcontainers;
910
import org.testcontainers.utility.DockerImageName;
1011

11-
import java.nio.file.Files;
12-
import java.nio.file.Path;
1312
import java.time.Duration;
1413

1514
import static org.junit.jupiter.api.Assertions.assertTrue;
1615

1716
@Testcontainers(disabledWithoutDocker = true)
17+
@ExtendWith(TestLogCaptureJunitExtension.class)
1818
public class ConnectIntegTest {
1919

2020
@Container
@@ -49,18 +49,6 @@ public void connectTest() {
4949

5050
Proxy.getInstance().connectAndCatchExceptions();
5151

52-
assertTrue(Proxy.getInstance().isConnected(), () -> {
53-
String zenithLog = "";
54-
try {
55-
zenithLog = Files.readString(Path.of("log/latest.log"));
56-
} catch (Exception e) {
57-
zenithLog = e.getMessage();
58-
}
59-
return "Failed to connect to local mc server: \n"
60-
+ minecraftServer.getLogs()
61-
+ "\n"
62-
+ zenithLog;
63-
}
64-
);
52+
assertTrue(Proxy.getInstance().isConnected(), "Failed to connect to local mc server");
6553
}
6654
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.zenith;
2+
3+
import ch.qos.logback.classic.spi.ILoggingEvent;
4+
import ch.qos.logback.classic.spi.ThrowableProxyUtil;
5+
6+
import java.time.Instant;
7+
import java.time.ZoneOffset;
8+
import java.time.format.DateTimeFormatter;
9+
import java.util.concurrent.ConcurrentLinkedDeque;
10+
import java.util.concurrent.atomic.AtomicLong;
11+
12+
public final class TestLogCapture {
13+
private static final int MAX_ENTRIES = 20_000;
14+
private static final DateTimeFormatter TS_FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneOffset.UTC);
15+
private static final AtomicLong SEQUENCE = new AtomicLong();
16+
private static final ConcurrentLinkedDeque<Entry> ENTRIES = new ConcurrentLinkedDeque<>();
17+
private static final Object TRIM_LOCK = new Object();
18+
19+
private TestLogCapture() {}
20+
21+
public static long checkpoint() {
22+
return SEQUENCE.get();
23+
}
24+
25+
public static String dumpSince(final long checkpoint) {
26+
var builder = new StringBuilder();
27+
for (var entry : ENTRIES) {
28+
if (entry.sequence <= checkpoint) continue;
29+
builder.append(entry.text);
30+
}
31+
return builder.isEmpty() ? "<no Zenith logs captured>" : builder.toString();
32+
}
33+
34+
static void append(final ILoggingEvent event) {
35+
var sequence = SEQUENCE.incrementAndGet();
36+
var text = format(event);
37+
ENTRIES.addLast(new Entry(sequence, text));
38+
39+
if (ENTRIES.size() <= MAX_ENTRIES) return;
40+
synchronized (TRIM_LOCK) {
41+
while (ENTRIES.size() > MAX_ENTRIES) {
42+
ENTRIES.pollFirst();
43+
}
44+
}
45+
}
46+
47+
private static String format(final ILoggingEvent event) {
48+
var timestamp = TS_FORMATTER.format(Instant.ofEpochMilli(event.getTimeStamp()));
49+
var builder = new StringBuilder(256)
50+
.append("[")
51+
.append(timestamp)
52+
.append("] [")
53+
.append(event.getLoggerName())
54+
.append("] [")
55+
.append(event.getLevel())
56+
.append("] [")
57+
.append(event.getThreadName())
58+
.append("] ")
59+
.append(event.getFormattedMessage())
60+
.append('\n');
61+
var throwableProxy = event.getThrowableProxy();
62+
if (throwableProxy != null) {
63+
builder.append(ThrowableProxyUtil.asString(throwableProxy));
64+
}
65+
return builder.toString();
66+
}
67+
68+
private record Entry(long sequence, String text) {}
69+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.zenith;
2+
3+
import ch.qos.logback.classic.spi.ILoggingEvent;
4+
import ch.qos.logback.core.AppenderBase;
5+
6+
public class TestLogCaptureAppender extends AppenderBase<ILoggingEvent> {
7+
@Override
8+
protected void append(final ILoggingEvent eventObject) {
9+
TestLogCapture.append(eventObject);
10+
}
11+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.zenith;
2+
3+
import org.junit.jupiter.api.extension.BeforeEachCallback;
4+
import org.junit.jupiter.api.extension.ExtensionContext;
5+
import org.junit.jupiter.api.extension.TestWatcher;
6+
7+
import java.util.concurrent.ConcurrentHashMap;
8+
import java.util.concurrent.ConcurrentMap;
9+
10+
public class TestLogCaptureJunitExtension implements BeforeEachCallback, TestWatcher {
11+
private static final ConcurrentMap<String, Long> CHECKPOINTS = new ConcurrentHashMap<>();
12+
13+
@Override
14+
public void beforeEach(final ExtensionContext context) {
15+
CHECKPOINTS.put(context.getUniqueId(), TestLogCapture.checkpoint());
16+
}
17+
18+
@Override
19+
public void testFailed(final ExtensionContext context, final Throwable cause) {
20+
var checkpoint = CHECKPOINTS.remove(context.getUniqueId());
21+
if (checkpoint == null)
22+
return;
23+
24+
var testName = context.getDisplayName();
25+
var logs = TestLogCapture.dumpSince(checkpoint);
26+
System.err.println("\n=== test failed: " + testName + " ===");
27+
System.err.println(logs);
28+
System.err.println("===\n");
29+
}
30+
31+
@Override
32+
public void testSuccessful(final ExtensionContext context) {
33+
CHECKPOINTS.remove(context.getUniqueId());
34+
}
35+
36+
@Override
37+
public void testAborted(final ExtensionContext context, final Throwable cause) {
38+
CHECKPOINTS.remove(context.getUniqueId());
39+
}
40+
}

src/test/resources/logback-test.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,16 @@
2828
<appender-ref ref="TERMINAL"/>
2929
<discardingThreshold>0</discardingThreshold>
3030
</appender>
31+
<appender name="TEST_CAPTURE" class="com.zenith.TestLogCaptureAppender">
32+
<filter class="com.zenith.terminal.logback.LogSourceFilter"/>
33+
<filter class="ThresholdFilter">
34+
<level>INFO</level>
35+
</filter>
36+
</appender>
37+
3138

3239
<root>
33-
<!-- allow logs from other libs to also go to stdout-->
3440
<appender-ref ref="ASYNC_TERMINAL"/>
41+
<appender-ref ref="TEST_CAPTURE"/>
3542
</root>
3643
</configuration>

0 commit comments

Comments
 (0)