|
| 1 | +package fr.insalyon.creatis.gasw; |
| 2 | + |
| 3 | +import ch.qos.logback.classic.LoggerContext; |
| 4 | +import ch.qos.logback.classic.util.ContextInitializer; |
| 5 | +import ch.qos.logback.core.joran.spi.JoranException; |
| 6 | +import org.junit.jupiter.api.AfterEach; |
| 7 | +import org.junit.jupiter.api.Assertions; |
| 8 | +import org.junit.jupiter.api.BeforeEach; |
| 9 | +import org.junit.jupiter.api.Test; |
| 10 | +import org.slf4j.Logger; |
| 11 | +import org.slf4j.LoggerFactory; |
| 12 | + |
| 13 | +import java.io.IOException; |
| 14 | +import java.net.URISyntaxException; |
| 15 | +import java.net.URL; |
| 16 | +import java.nio.file.Files; |
| 17 | +import java.nio.file.Path; |
| 18 | +import java.nio.file.Paths; |
| 19 | +import java.util.Arrays; |
| 20 | +import java.util.List; |
| 21 | +import java.util.stream.Collectors; |
| 22 | +import java.util.stream.Stream; |
| 23 | + |
| 24 | +/** |
| 25 | + * We need to limit the log size to avoid them to make a full on disk when there is an infinite loop with an error |
| 26 | + * (this happened) |
| 27 | + * At first, I wanted to configure logback to keep only the beginning of the logs and stop once a limit is attained. |
| 28 | + * This does not seem to be possible (easily at least) |
| 29 | + * So I did a classic config, with a simple rolling (only 1 rolled file) and a size limit. |
| 30 | + * So logback will a log file workflow.out (and workflow.err), and move it to workflow-1.out (and workflow-1.err) once |
| 31 | + * it gets bigger than the limit (deleting the previous workflow-1.out/err if they existed). |
| 32 | + * |
| 33 | + * We test that here by : |
| 34 | + * - using a logback-test.xml config file only used in test |
| 35 | + * - ensure that this behaves as expected with a limit of 1KB |
| 36 | + * - then we ensure that the logback.xml config file used in production is almost the same as logback-test.xml except |
| 37 | + * the small stuff necessary for the tests (size of 1KB instead of 100MB, and check every millisecond) |
| 38 | + * |
| 39 | + * Also, to test a clean context, we delete the logs files before the test. |
| 40 | + * We also have to reload logback after that because logback won't recreate the manually deleted log files. |
| 41 | + * |
| 42 | + */ |
| 43 | +public class GaswLoggerTest { |
| 44 | + |
| 45 | + @Test |
| 46 | + public void verifyProdConfigIsTheSameAsTheTestOne() throws IOException, URISyntaxException { |
| 47 | + // compare logback-test.xml (used in the next test) and logback.xml (used in prod) |
| 48 | + // only small test stuff must change |
| 49 | + URL prodConfigURL = getClass().getResource("/logback.xml"); |
| 50 | + URL testConfigURL = getClass().getResource("/logback-test.xml"); |
| 51 | + List<String> prodConfig = Files.readAllLines(Paths.get(prodConfigURL.toURI())); |
| 52 | + List<String> testConfig = Files.readAllLines(Paths.get(testConfigURL.toURI())); |
| 53 | + |
| 54 | + // in test : remove the checkIncrement lines, flagged with a suffix |
| 55 | + testConfig.removeIf(line -> line.trim().endsWith("<!-- only for test -->")); |
| 56 | + |
| 57 | + // also change and assert the max size is the expected one |
| 58 | + // assert the size is only configured twice (stdout and stderr) |
| 59 | + Assertions.assertEquals(2, testConfig.stream().filter(line -> line.trim().startsWith("<maxFileSize>")).count()); |
| 60 | + testConfig = testConfig.stream().map(line -> { |
| 61 | + if ( ! line.trim().startsWith("<maxFileSize>")) { |
| 62 | + return line; |
| 63 | + } |
| 64 | + return line.replace("1KB", "100MB"); |
| 65 | + }).collect(Collectors.toList()); |
| 66 | + |
| 67 | + Assertions.assertIterableEquals(testConfig, prodConfig); |
| 68 | + } |
| 69 | + |
| 70 | + @Test |
| 71 | + public void testLogFileSizeLimit() throws IOException, InterruptedException { |
| 72 | + Logger logger = LoggerFactory.getLogger(Gasw.class); |
| 73 | + // 100 log lines should take around 10KB |
| 74 | + for (int i = 0; i<100; i++) { |
| 75 | + // wait a little because logback has a timeout and do not verify the size if the logs are too close |
| 76 | + // in logback-test.xml, logcback si configured to test every millisecond |
| 77 | + Thread.sleep(2); |
| 78 | + logger.error(i + " / This test line should take around 100B"); |
| 79 | + } |
| 80 | + // verify |
| 81 | + assertLogFiles(); |
| 82 | + } |
| 83 | + |
| 84 | + @BeforeEach |
| 85 | + @AfterEach |
| 86 | + public void cleanLogFiles() throws IOException, JoranException { |
| 87 | + try (Stream<Path> stream = Files.list(Paths.get(""))) { |
| 88 | + stream |
| 89 | + .filter(file -> ! Files.isDirectory(file)) |
| 90 | + .filter(file -> file.getFileName().toString().startsWith("workflow")) |
| 91 | + .forEach(f -> { |
| 92 | + System.out.println("deleting : " + f.getFileName()); |
| 93 | + f.toFile().delete(); |
| 94 | + }); |
| 95 | + } |
| 96 | + /* |
| 97 | + If we do not reload, as we delete the log file after logback has started (in other tests), |
| 98 | + logback will not re-create them. |
| 99 | + */ |
| 100 | + System.out.println("reloading logback"); |
| 101 | + reloadLogback(); |
| 102 | + } |
| 103 | + |
| 104 | + public void reloadLogback() throws JoranException { |
| 105 | + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); |
| 106 | + context.reset(); |
| 107 | + ContextInitializer initializer = new ContextInitializer(context); |
| 108 | + initializer.autoConfig(); |
| 109 | + } |
| 110 | + |
| 111 | + public void assertLogFiles() throws IOException { |
| 112 | + List<Path> logFiles; |
| 113 | + try (Stream<Path> stream = Files.list(Paths.get(""))) { |
| 114 | + logFiles = stream |
| 115 | + .filter(file -> ! Files.isDirectory(file)) |
| 116 | + .filter(file -> file.getFileName().toString().startsWith("workflow")) |
| 117 | + .collect(Collectors.toList()); |
| 118 | + |
| 119 | + } |
| 120 | + // we should get only 1 log file and 1 roll-over for .out and .err |
| 121 | + List<String> authorizedNames = Arrays.asList("workflow.out", "workflow-1.out", "workflow.err", "workflow-1.err"); |
| 122 | + for (Path logFile : logFiles) { |
| 123 | + Assertions.assertTrue( |
| 124 | + authorizedNames.contains(logFile.getFileName().toString()), |
| 125 | + "a log file is not workflow.out or workflow.err : " + logFile); |
| 126 | + } |
| 127 | + // rollover at 1KB. it must be 1 line bigger than 1KB, so not bigger than 1300 bytes with a margin |
| 128 | + for (Path logFile : logFiles) { |
| 129 | + Assertions.assertTrue( |
| 130 | + Files.size(logFile) < 1300, |
| 131 | + "a log file is too much bigger than 1KB : " + logFile |
| 132 | + + " / size " + Files.size(logFile)); |
| 133 | + } |
| 134 | + // verify logs are done |
| 135 | + for (String logFileName : authorizedNames) { |
| 136 | + Path logFile = Paths.get(logFileName); |
| 137 | + Assertions.assertTrue(Files.exists(logFile), "Missing log file " + logFile); |
| 138 | + |
| 139 | + // there could be only 1 line if rolling has just happened |
| 140 | + List<String> logLines = Files.readAllLines(logFile); |
| 141 | + Assertions.assertTrue(logLines.size() > 0, "a log file is too small : " + logFileName); |
| 142 | + Assertions.assertTrue( |
| 143 | + logLines.get(0).contains("This test line should take around 100B"), |
| 144 | + "a log file does not contain the test log " + logFileName); |
| 145 | + } |
| 146 | + } |
| 147 | +} |
| 148 | + |
0 commit comments