From 54be65a11c5fdb29b0ad2fba929c8a2d932a7cda Mon Sep 17 00:00:00 2001 From: "tomasz.kowalczewski" Date: Fri, 12 Mar 2021 23:32:25 +0100 Subject: [PATCH] Re #36: More tests based on log4j tests. Changed test that erroneously set wait time to 10000 seconds. Fixed bug in logback and log4j2 versions that caused Tjahzi to complain about wring buffer size if it was in fact of correct size. --- .../tjahzi/log4j2/LokiAppenderBuilder.java | 2 +- .../LogLevelLabelConfigurationTest.java | 2 +- .../log4j2/LokiAppenderMonitoringTest.java | 3 + .../resources/appender-test-large-batches.xml | 2 +- logback-appender/pom.xml | 7 + .../pl/tkowalcz/tjahzi/logback/Label.java | 15 ++ .../tkowalcz/tjahzi/logback/LokiAppender.java | 18 +++ .../tjahzi/logback/LokiAppenderFactory.java | 9 +- .../logback/BufferSizeConfigurationTest.java | 58 +++++++ .../tjahzi/logback/LabelFactoryTest.java | 145 ++++++++++++++++++ .../pl/tkowalcz/tjahzi/logback/LabelTest.java | 21 +++ .../LogLevelLabelConfigurationTest.java | 141 +++++++++++++++++ .../logback/LokiAppenderMonitoringTest.java | 68 ++++++++ .../logback/LokiAppenderShutdownTest.java | 84 ++++++++++ .../tjahzi/logback/util/TestUtil.java | 26 ++++ .../resources/appender-test-large-batches.xml | 2 +- 16 files changed, 597 insertions(+), 6 deletions(-) create mode 100644 logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/BufferSizeConfigurationTest.java create mode 100644 logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LabelFactoryTest.java create mode 100644 logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LabelTest.java create mode 100644 logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LogLevelLabelConfigurationTest.java create mode 100644 logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LokiAppenderMonitoringTest.java create mode 100644 logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LokiAppenderShutdownTest.java create mode 100644 logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/util/TestUtil.java diff --git a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/LokiAppenderBuilder.java b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/LokiAppenderBuilder.java index 3355965..3353fd8 100644 --- a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/LokiAppenderBuilder.java +++ b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/LokiAppenderBuilder.java @@ -101,7 +101,7 @@ public class LokiAppenderBuilder> extends Abstr ); int bufferSizeBytes = getBufferSizeMegabytes() * BYTES_IN_MEGABYTE; - if (TjahziInitializer.isCorrectSize(bufferSizeBytes)) { + if (!TjahziInitializer.isCorrectSize(bufferSizeBytes)) { LOGGER.warn("Invalid log buffer size {} - using nearest power of two greater than provided value, no less than 1MB. {}", bufferSizeBytes, GitHubDocs.LOG_BUFFER_SIZING.getLogMessage() diff --git a/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/LogLevelLabelConfigurationTest.java b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/LogLevelLabelConfigurationTest.java index 36dddb2..219ce86 100644 --- a/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/LogLevelLabelConfigurationTest.java +++ b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/LogLevelLabelConfigurationTest.java @@ -37,7 +37,7 @@ class LogLevelLabelConfigurationTest { .withExposedPorts(3100); @Test - void shouldWorkWIthNoLogLevelConfigured() throws URISyntaxException { + void shouldWorkWIthNoLogLevelConfigured() throws Exception { // Given System.setProperty("loki.host", loki.getHost()); System.setProperty("loki.port", loki.getFirstMappedPort().toString()); diff --git a/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/LokiAppenderMonitoringTest.java b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/LokiAppenderMonitoringTest.java index 4bdbd95..fd255cb 100644 --- a/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/LokiAppenderMonitoringTest.java +++ b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/LokiAppenderMonitoringTest.java @@ -21,6 +21,9 @@ class LokiAppenderMonitoringTest { @Test void shouldInjectMonitoringAndUseIt() throws URISyntaxException { // Given + System.setProperty("loki.host", "somewhere"); + System.setProperty("loki.port", "42"); + URI uri = getClass() .getClassLoader() .getResource("basic-appender-test-configuration.xml") diff --git a/log4j2-appender/src/test/resources/appender-test-large-batches.xml b/log4j2-appender/src/test/resources/appender-test-large-batches.xml index 222f1de..b418b5a 100644 --- a/log4j2-appender/src/test/resources/appender-test-large-batches.xml +++ b/log4j2-appender/src/test/resources/appender-test-large-batches.xml @@ -7,7 +7,7 @@ - + 1048576 ${sys:loki.host} diff --git a/logback-appender/pom.xml b/logback-appender/pom.xml index a8f1b14..0931358 100644 --- a/logback-appender/pom.xml +++ b/logback-appender/pom.xml @@ -27,6 +27,13 @@ 1.2.3 + + io.dropwizard.metrics + metrics-core + 4.1.17 + provided + + com.google.code.java-allocation-instrumenter java-allocation-instrumenter diff --git a/logback-appender/src/main/java/pl/tkowalcz/tjahzi/logback/Label.java b/logback-appender/src/main/java/pl/tkowalcz/tjahzi/logback/Label.java index dbb5028..25202c9 100644 --- a/logback-appender/src/main/java/pl/tkowalcz/tjahzi/logback/Label.java +++ b/logback-appender/src/main/java/pl/tkowalcz/tjahzi/logback/Label.java @@ -1,5 +1,6 @@ package pl.tkowalcz.tjahzi.logback; +import java.util.Map; import java.util.regex.Pattern; public class Label { @@ -9,6 +10,15 @@ public class Label { private String name; private String value; + // For Logback this is just a convenience method for testing + public static Label createLabel(String name, String value) { + Label result = new Label(); + result.setName(name); + result.setValue(value); + + return result; + } + public boolean hasValidName() { return hasValidName(getName()); } @@ -29,6 +39,11 @@ public class Label { this.value = value; } + // @VisibleForTests + public Map.Entry asEntry() { + return Map.entry(getName(), getValue()); + } + public static boolean hasValidName(String label) { return LABEL_NAME_PATTER.matcher(label).matches(); } diff --git a/logback-appender/src/main/java/pl/tkowalcz/tjahzi/logback/LokiAppender.java b/logback-appender/src/main/java/pl/tkowalcz/tjahzi/logback/LokiAppender.java index 7d1dbad..8a7f3cf 100644 --- a/logback-appender/src/main/java/pl/tkowalcz/tjahzi/logback/LokiAppender.java +++ b/logback-appender/src/main/java/pl/tkowalcz/tjahzi/logback/LokiAppender.java @@ -4,17 +4,21 @@ import ch.qos.logback.classic.encoder.PatternLayoutEncoder; import ch.qos.logback.classic.spi.ILoggingEvent; import pl.tkowalcz.tjahzi.LoggingSystem; import pl.tkowalcz.tjahzi.TjahziLogger; +import pl.tkowalcz.tjahzi.stats.MonitoringModule; +import pl.tkowalcz.tjahzi.stats.MutableMonitoringModuleWrapper; import java.nio.ByteBuffer; import java.util.Collections; public class LokiAppender extends LokiAppenderConfigurator { + private PatternLayoutEncoder encoder; private LoggingSystem loggingSystem; private TjahziLogger logger; private String logLevelLabel; + private MutableMonitoringModuleWrapper monitoringModuleWrapper; public PatternLayoutEncoder getEncoder() { return encoder; @@ -24,6 +28,19 @@ public class LokiAppender extends LokiAppenderConfigurator { this.encoder = encoder; } + /** + * This is an entry point to set monitoring (statistics) hooks for this appender. This + * API is in beta and is subject to change (and probably will). + */ + public void setMonitoringModule(MonitoringModule monitoringModule) { + monitoringModuleWrapper.setMonitoringModule(monitoringModule); + } + + // @VisibleForTesting + public LoggingSystem getLoggingSystem() { + return loggingSystem; + } + @Override protected void append(ILoggingEvent event) { String logLevel = event.getLevel().toString(); @@ -50,6 +67,7 @@ public class LokiAppender extends LokiAppenderConfigurator { LokiAppenderFactory lokiAppenderFactory = new LokiAppenderFactory(this); loggingSystem = lokiAppenderFactory.createAppender(); logLevelLabel = lokiAppenderFactory.getLogLevelLabel(); + monitoringModuleWrapper = lokiAppenderFactory.getMonitoringModuleWrapper(); logger = loggingSystem.createLogger(); loggingSystem.start(); diff --git a/logback-appender/src/main/java/pl/tkowalcz/tjahzi/logback/LokiAppenderFactory.java b/logback-appender/src/main/java/pl/tkowalcz/tjahzi/logback/LokiAppenderFactory.java index 03428f4..1a4e961 100644 --- a/logback-appender/src/main/java/pl/tkowalcz/tjahzi/logback/LokiAppenderFactory.java +++ b/logback-appender/src/main/java/pl/tkowalcz/tjahzi/logback/LokiAppenderFactory.java @@ -19,6 +19,7 @@ public class LokiAppenderFactory { private final HashMap lokiLabels; private final String logLevelLabel; + private MutableMonitoringModuleWrapper monitoringModuleWrapper; public LokiAppenderFactory(LokiAppenderConfigurator configurator) { this.configurator = configurator; @@ -47,7 +48,7 @@ public class LokiAppenderFactory { .flatMap(header -> Stream.of(header.getName(), header.getValue())) .toArray(String[]::new); - MutableMonitoringModuleWrapper monitoringModuleWrapper = new MutableMonitoringModuleWrapper(); + monitoringModuleWrapper = new MutableMonitoringModuleWrapper(); monitoringModuleWrapper.setMonitoringModule(new StandardMonitoringModule()); NettyHttpClient httpClient = HttpClientFactory @@ -59,7 +60,7 @@ public class LokiAppenderFactory { ); int bufferSizeBytes = configurator.getBufferSizeMegabytes() * LokiAppenderConfigurator.BYTES_IN_MEGABYTE; - if (TjahziInitializer.isCorrectSize(bufferSizeBytes)) { + if (!TjahziInitializer.isCorrectSize(bufferSizeBytes)) { configurator.addWarn( String.format( "Invalid log buffer size %d - using nearest power of two greater than provided value, no less than 1MB. %s\n", @@ -83,4 +84,8 @@ public class LokiAppenderFactory { public String getLogLevelLabel() { return logLevelLabel; } + + public MutableMonitoringModuleWrapper getMonitoringModuleWrapper() { + return monitoringModuleWrapper; + } } diff --git a/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/BufferSizeConfigurationTest.java b/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/BufferSizeConfigurationTest.java new file mode 100644 index 0000000..bacb406 --- /dev/null +++ b/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/BufferSizeConfigurationTest.java @@ -0,0 +1,58 @@ +package pl.tkowalcz.tjahzi.logback; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import org.junit.jupiter.api.Test; +import org.slf4j.LoggerFactory; +import pl.tkowalcz.tjahzi.logback.util.TestUtil; + +import java.net.URI; + +import static org.assertj.core.api.Assertions.assertThat; + +class BufferSizeConfigurationTest { + + @Test + void shouldUseDefaultWhenLogBufferSizeIsNotSetViaConfiguration() throws Exception { + // Given + URI uri = getClass() + .getClassLoader() + .getResource("appender-test-with-buffer-size-megabytes-unset.xml") + .toURI(); + + // When + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(context); + + context.reset(); + configurator.doConfigure(uri.toURL()); + + // Then + LokiAppender loki = TestUtil.getLokiAppender(context); + assertThat(loki.getLoggingSystem().getLogBufferSize()).isEqualTo(32 * 1024 * 1024); + } + + @Test + void shouldSetLogBufferSizeViaConfiguration() throws Exception { + // Given + URI uri = getClass() + .getClassLoader() + .getResource("appender-test-with-buffer-size-megabytes-set-to-64.xml") + .toURI(); + + // When + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(context); + + context.reset(); + configurator.doConfigure(uri.toURL()); + + // Then + LokiAppender loki = TestUtil.getLokiAppender(context); + assertThat(loki.getLoggingSystem().getLogBufferSize()).isEqualTo(64 * 1024 * 1024); + } +} diff --git a/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LabelFactoryTest.java b/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LabelFactoryTest.java new file mode 100644 index 0000000..20bad63 --- /dev/null +++ b/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LabelFactoryTest.java @@ -0,0 +1,145 @@ +package pl.tkowalcz.tjahzi.logback; + +import ch.qos.logback.core.ConsoleAppender; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class LabelFactoryTest { + + @Test + void shouldRemoveDuplicatedLabels() { + // Given + Label thisIsADuplicateAndShouldBeDropped = Label.createLabel("ip", "127.0.0.1"); + Label thisShouldStay = Label.createLabel("ip", "10.0.2.34"); + Label thisTooShouldStay = Label.createLabel("hostname", "hostname"); + + LabelFactory labelFactory = new LabelFactory( + new ConsoleAppender<>(), + "log_level", + thisIsADuplicateAndShouldBeDropped, + thisShouldStay, + thisTooShouldStay + ); + + // When + HashMap actual = labelFactory.convertLabelsDroppingInvalid(); + + // Then + assertThat(actual).containsOnly( + thisShouldStay.asEntry(), + thisTooShouldStay.asEntry() + ); + } + + @Test + void shouldSkipLabelsWithInvalidName() { + // Given + Label thisShouldStay = Label.createLabel("ip", "10.0.2.34"); + Label thisShouldStayToo = Label.createLabel("region", "coruscant-west-2"); + Label invalidNameShouldBeDropped = Label.createLabel("host-name", "hostname"); + + LabelFactory labelFactory = new LabelFactory( + new ConsoleAppender<>(), + "log_level", + thisShouldStayToo, + thisShouldStay, + invalidNameShouldBeDropped + ); + + // When + HashMap actual = labelFactory.convertLabelsDroppingInvalid(); + + // Then + assertThat(actual).containsOnly( + thisShouldStay.asEntry(), + thisShouldStayToo.asEntry() + ); + } + + @Test + void shouldAcceptNullLogLevel() { + // Given + Label label1 = Label.createLabel("ip", "10.0.2.34"); + Label label2 = Label.createLabel("region", "coruscant-west-2"); + + LabelFactory labelFactory = new LabelFactory( + new ConsoleAppender<>(), + null, + label2, + label1 + ); + + // When + HashMap actual = labelFactory.convertLabelsDroppingInvalid(); + + // Then + assertThat(actual).containsOnly( + label1.asEntry(), + label2.asEntry() + ); + } + + @Test + void logLevelLabelShouldOverrideConflictingLabel() { + // Given + String logLevelLabel = "log_level"; + + Label thisShouldStay = Label.createLabel("ip", "10.0.2.34"); + Label thisShouldBeRemovedDueToConflict = Label.createLabel(logLevelLabel, "coruscant-west-2"); + + LabelFactory labelFactory = new LabelFactory( + new ConsoleAppender<>(), + logLevelLabel, + thisShouldBeRemovedDueToConflict, + thisShouldStay + ); + + HashMap labels = new HashMap<>( + Map.ofEntries( + thisShouldStay.asEntry(), + thisShouldBeRemovedDueToConflict.asEntry() + ) + ); + + // When + String actual = labelFactory.validateLogLevelLabel(labels); + + // Then + assertThat(labels).containsOnly( + thisShouldStay.asEntry() + ); + + assertThat(actual).isEqualTo(logLevelLabel); + } + + @Test + void shouldDisableLogLevelLabelIfItHasInvalidName() { + // Given + String logLevelLabel = "log-level"; + + LabelFactory labelFactory = new LabelFactory(new ConsoleAppender<>(), logLevelLabel); + + // When + String actual = labelFactory.validateLogLevelLabel(new HashMap<>()); + + // Then + assertThat(actual).isNull(); + } + + @Test + void shouldHandleDisabledLogLevelLabel() { + // Given + String logLevelLabel = null; + LabelFactory labelFactory = new LabelFactory(new ConsoleAppender<>(), logLevelLabel); + + // When + String actual = labelFactory.validateLogLevelLabel(new HashMap<>()); + + // Then + assertThat(actual).isNull(); + } +} diff --git a/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LabelTest.java b/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LabelTest.java new file mode 100644 index 0000000..9195cdb --- /dev/null +++ b/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LabelTest.java @@ -0,0 +1,21 @@ +package pl.tkowalcz.tjahzi.logback; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class LabelTest { + + @Test + void shouldValidateNamePattern() { + // Given + Label correctLabel = Label.createLabel("aaa_bbb_ccc", "fobar"); + Label tooShortLabel = Label.createLabel("", "foobar"); + Label labelWithInvalidChars = Label.createLabel("log-level", "foobar"); + + // When & Then + assertThat(correctLabel.hasValidName()).isTrue(); + assertThat(tooShortLabel.hasValidName()).isFalse(); + assertThat(labelWithInvalidChars.hasValidName()).isFalse(); + } +} diff --git a/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LogLevelLabelConfigurationTest.java b/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LogLevelLabelConfigurationTest.java new file mode 100644 index 0000000..c2d1c1d --- /dev/null +++ b/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LogLevelLabelConfigurationTest.java @@ -0,0 +1,141 @@ +package pl.tkowalcz.tjahzi.logback; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import io.restassured.parsing.Parser; +import org.awaitility.Awaitility; +import org.awaitility.Durations; +import org.junit.jupiter.api.Test; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.BindMode; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.net.URI; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.nullValue; + +@Testcontainers +class LogLevelLabelConfigurationTest { + + @Container + public GenericContainer loki = new GenericContainer("grafana/loki:latest") + .withCommand("-config.file=/etc/loki-config.yaml") + .withClasspathResourceMapping("loki-config.yaml", + "/etc/loki-config.yaml", + BindMode.READ_ONLY) + .waitingFor( + Wait.forHttp("/ready") + .forPort(3100) + ) + .withExposedPorts(3100); + + @Test + void shouldWorkWIthNoLogLevelConfigured() throws Exception { + // Given + System.setProperty("loki.host", loki.getHost()); + System.setProperty("loki.port", loki.getFirstMappedPort().toString()); + + URI uri = getClass() + .getClassLoader() + .getResource("appender-test-with-log-label-unset.xml") + .toURI(); + + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(context); + + context.reset(); + configurator.doConfigure(uri.toURL()); + + // When + Logger logger = context.getLogger(LokiAppenderLargeBatchesTest.class); + logger.info("Test"); + + // Then + RestAssured.port = loki.getFirstMappedPort(); + RestAssured.baseURI = "http://" + loki.getHost(); + RestAssured.registerParser("text/plain", Parser.JSON); + + Awaitility + .await() + .atMost(Durations.TEN_SECONDS) + .pollInterval(Durations.ONE_SECOND) + .ignoreExceptions() + .untilAsserted(() -> { + given() + .contentType(ContentType.URLENC) + .urlEncodingEnabled(false) + .formParam("query=%7Bserver%3D%22127.0.0.1%22%7D") + .when() + .get("/loki/api/v1/query_range") + .then() + .log() + .all() + .statusCode(200) + .body("status", equalTo("success")) + .body("data.result.size()", equalTo(1)) + .body("data.result[0].stream.server", equalTo("127.0.0.1")) + .body("data.result[0].stream.log_level", nullValue()); + }); + } + + @Test + void shouldSendLogLevelAsConfigured() throws Exception { + // Given + System.setProperty("loki.host", loki.getHost()); + System.setProperty("loki.port", loki.getFirstMappedPort().toString()); + + URI uri = getClass() + .getClassLoader() + .getResource("appender-test-with-log-label-set.xml") + .toURI(); + + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(context); + + context.reset(); + configurator.doConfigure(uri.toURL()); + + // When + Logger logger = context.getLogger(LokiAppenderLargeBatchesTest.class); + logger.info("Test"); + + // Then + RestAssured.port = loki.getFirstMappedPort(); + RestAssured.baseURI = "http://" + loki.getHost(); + RestAssured.registerParser("text/plain", Parser.JSON); + + Awaitility + .await() + .atMost(Durations.TEN_SECONDS) + .pollInterval(Durations.ONE_SECOND) + .ignoreExceptions() + .untilAsserted(() -> { + given() + .contentType(ContentType.URLENC) + .urlEncodingEnabled(false) + .formParam("query=%7Bserver%3D%22127.0.0.1%22%7D") + .when() + .get("/loki/api/v1/query_range") + .then() + .log() + .all() + .statusCode(200) + .body("status", equalTo("success")) + .body("data.result.size()", equalTo(1)) + .body("data.result[0].stream.server", equalTo("127.0.0.1")) + .body("data.result[0].stream.log_level", equalTo("INFO")); + }); + } +} diff --git a/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LokiAppenderMonitoringTest.java b/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LokiAppenderMonitoringTest.java new file mode 100644 index 0000000..68b28a6 --- /dev/null +++ b/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LokiAppenderMonitoringTest.java @@ -0,0 +1,68 @@ +package pl.tkowalcz.tjahzi.logback; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import com.codahale.metrics.Counter; +import com.codahale.metrics.MetricRegistry; +import org.junit.jupiter.api.Test; +import org.slf4j.LoggerFactory; +import org.testcontainers.junit.jupiter.Testcontainers; +import pl.tkowalcz.tjahzi.logback.util.TestUtil; +import pl.tkowalcz.tjahzi.stats.DropwizardMonitoringModule; + +import java.net.URI; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +@Testcontainers +class LokiAppenderMonitoringTest { + + @Test + void shouldInjectMonitoringAndUseIt() throws Exception { + // Given + System.setProperty("loki.host", "somewhere"); + System.setProperty("loki.port", "42"); + + URI uri = getClass() + .getClassLoader() + .getResource("basic-appender-test-configuration.xml") + .toURI(); + + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(context); + + context.reset(); + configurator.doConfigure(uri.toURL()); + + LokiAppender loki = TestUtil.getLokiAppender(context); + + MetricRegistry metricRegistry = new MetricRegistry(); + loki.setMonitoringModule( + new DropwizardMonitoringModule( + metricRegistry, + "appender.loki" + ) + ); + + // When + Logger logger = context.getLogger(LokiAppenderLargeBatchesTest.class); + logger.info("Test test test"); + + // Then + assertThat(metricRegistry.getMetrics()).isNotEmpty(); + + Counter connectAttemptsCounter = metricRegistry + .getCounters() + .get("appender.loki.httpConnectAttempts"); + assertThat(connectAttemptsCounter).isNotNull(); + + await().untilAsserted(() -> { + assertThat(connectAttemptsCounter.getCount()).isEqualTo(1); + } + ); + } +} diff --git a/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LokiAppenderShutdownTest.java b/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LokiAppenderShutdownTest.java new file mode 100644 index 0000000..0942876 --- /dev/null +++ b/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/LokiAppenderShutdownTest.java @@ -0,0 +1,84 @@ +package pl.tkowalcz.tjahzi.logback; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.Test; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.BindMode; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.net.URI; + +import static org.assertj.core.api.Assertions.assertThat; + +@Testcontainers +class LokiAppenderShutdownTest { + + @Container + public GenericContainer loki = new GenericContainer("grafana/loki:latest") + .withCommand("-config.file=/etc/loki-config.yaml") + .withClasspathResourceMapping("loki-config.yaml", + "/etc/loki-config.yaml", + BindMode.READ_ONLY) + .waitingFor( + Wait.forHttp("/ready") + .forPort(3100) + ) + .withExposedPorts(3100); + + @Test + void shouldSendData() throws Exception { + // Given + System.setProperty("loki.host", loki.getHost()); + System.setProperty("loki.port", loki.getFirstMappedPort().toString()); + + URI uri = getClass() + .getClassLoader() + .getResource("basic-appender-test-configuration.xml") + .toURI(); + + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(context); + + context.reset(); + configurator.doConfigure(uri.toURL()); + + // Verify our assumptions that we can find threads started by Tjahzi + Awaitility.await().untilAsserted(() -> { + ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false); + + assertThat(threadInfos) + .extracting(ThreadInfo::getThreadName) + .contains( + "ReadingLogBufferAndSendingHttp", + "tjahzi-worker" + ); + }); + + // When + context.stop(); + + // Then + Awaitility.await().untilAsserted(() -> { + ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false); + + assertThat(threadInfos) + .extracting(ThreadInfo::getThreadName) + .doesNotContain( + "ReadingLogBufferAndSendingHttp", + "tjahzi-worker" + ); + }); + } +} diff --git a/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/util/TestUtil.java b/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/util/TestUtil.java new file mode 100644 index 0000000..42abe13 --- /dev/null +++ b/logback-appender/src/test/java/pl/tkowalcz/tjahzi/logback/util/TestUtil.java @@ -0,0 +1,26 @@ +package pl.tkowalcz.tjahzi.logback.util; + +import ch.qos.logback.classic.LoggerContext; +import pl.tkowalcz.tjahzi.logback.LokiAppender; + +import java.util.Spliterators; +import java.util.stream.StreamSupport; + +public class TestUtil { + + public static LokiAppender getLokiAppender(LoggerContext context) { + return (LokiAppender) context + .getLoggerList() + .stream() + .flatMap( + logger -> + StreamSupport.stream( + Spliterators.spliteratorUnknownSize(logger.iteratorForAppenders(), 0), + false + ) + ) + .filter(appender -> appender instanceof LokiAppender) + .findAny() + .orElseThrow(() -> new AssertionError("Expected to find Loki appender")); + } +} diff --git a/logback-appender/src/test/resources/appender-test-large-batches.xml b/logback-appender/src/test/resources/appender-test-large-batches.xml index 6d87c8e..4590b57 100644 --- a/logback-appender/src/test/resources/appender-test-large-batches.xml +++ b/logback-appender/src/test/resources/appender-test-large-batches.xml @@ -2,7 +2,7 @@ 2 - 10000 + 10 1048576 ${loki.host}