diff --git a/core/src/main/java/pl/tkowalcz/tjahzi/http/RequestAndResponseHandler.java b/core/src/main/java/pl/tkowalcz/tjahzi/http/RequestAndResponseHandler.java index bf963c5..d5811d1 100644 --- a/core/src/main/java/pl/tkowalcz/tjahzi/http/RequestAndResponseHandler.java +++ b/core/src/main/java/pl/tkowalcz/tjahzi/http/RequestAndResponseHandler.java @@ -38,7 +38,6 @@ public void channelRead(ChannelHandlerContext ctx, Object object) { monitoringModule.incrementHttpResponses(); if (msg.status().codeClass() != HttpStatusClass.SUCCESS) { - System.out.println(msg.content().toString(Charset.defaultCharset())); monitoringModule.incrementHttpErrors( msg.status().code(), msg.content().toString(Charset.defaultCharset()) diff --git a/pom.xml b/pom.xml index 24f78db..8674760 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,8 @@ core loki-protobuf + reload4j-appender + log4j2-appender log4j2-appender-nodep @@ -58,11 +60,11 @@ UTF-8 - 8 - 8 + 11 + 11 - 9 - 9 + 11 + 11 ${env.GPG_PASSPHRASE} diff --git a/reload4j-appender/pom.xml b/reload4j-appender/pom.xml new file mode 100644 index 0000000..740f2c1 --- /dev/null +++ b/reload4j-appender/pom.xml @@ -0,0 +1,145 @@ + + + 4.0.0 + + + pl.tkowalcz.tjahzi + tjahzi-parent + 0.9.33-SNAPSHOT + + + reload4j-appender + reload4j-appender + jar + + + + pl.tkowalcz.tjahzi + core + 0.9.33-SNAPSHOT + + + + ch.qos.reload4j + reload4j + 1.2.25 + + + + io.dropwizard.metrics + metrics-core + 4.1.17 + provided + + + + + org.slf4j + slf4j-reload4j + 2.0.7 + test + + + + org.mockito + mockito-core + 3.9.0 + test + + + + com.google.code.java-allocation-instrumenter + java-allocation-instrumenter + 3.3.0 + test + + + io.rest-assured + rest-assured + 4.3.1 + test + + + org.awaitility + awaitility + 4.0.3 + test + + + org.assertj + assertj-core + 3.15.0 + test + + + + + org.junit.jupiter + junit-jupiter-api + 5.6.2 + test + + + org.junit.jupiter + junit-jupiter-params + 5.6.2 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.6.2 + test + + + org.testcontainers + testcontainers + 1.14.3 + test + + + org.testcontainers + junit-jupiter + 1.14.3 + test + + + org.testcontainers + nginx + 1.14.3 + test + + + com.alibaba + dns-cache-manipulator + 1.8.1 + test + + + + + + allocation-profiling + + + + org.apache.maven.plugins + maven-surefire-plugin + + + -javaagent:"${settings.localRepository}/com/google/code/java-allocation-instrumenter/java-allocation-instrumenter/3.3.0/java-allocation-instrumenter-3.3.0.jar" + + + + + com.google.code.java-allocation-instrumenter + java-allocation-instrumenter + 3.3.0 + + + + + + + + diff --git a/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/CommaDelimitedListParser.java b/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/CommaDelimitedListParser.java new file mode 100644 index 0000000..5859d19 --- /dev/null +++ b/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/CommaDelimitedListParser.java @@ -0,0 +1,42 @@ +package pl.tkowalcz.tjahzi.reload4j; + +import org.apache.log4j.helpers.LogLog; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.BiFunction; + +public class CommaDelimitedListParser { + + public static List parseString( + String commaDelimitedList, + BiFunction converter) { + if (commaDelimitedList == null || commaDelimitedList.trim().isEmpty()) { + return Collections.emptyList(); + } + + ArrayList result = new ArrayList<>(); + String[] keyValuePairs = commaDelimitedList.split(","); + for (String keyValuePair : keyValuePairs) { + String[] keyAndValue = keyValuePair.split(":", 2); + + if (keyAndValue.length != 2) { + LogLog.warn("Invalid key-value pair format: " + keyValuePair); + continue; + } + + String key = keyAndValue[0].trim(); + String value = keyAndValue[1].trim(); + + if (key.isEmpty() || value.isEmpty()) { + LogLog.warn("Key or value cannot be empty: " + keyValuePair); + continue; + } + + result.add(converter.apply(key, value)); + } + + return result; + } +} diff --git a/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/Header.java b/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/Header.java new file mode 100644 index 0000000..d9d4ae8 --- /dev/null +++ b/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/Header.java @@ -0,0 +1,28 @@ +package pl.tkowalcz.tjahzi.reload4j; + +public class Header { + + private String name; + private String value; + + public Header(String name, String value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/Label.java b/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/Label.java new file mode 100644 index 0000000..f28ce0c --- /dev/null +++ b/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/Label.java @@ -0,0 +1,43 @@ +package pl.tkowalcz.tjahzi.reload4j; + +import java.util.regex.Pattern; + +public class Label { + + private static final Pattern LABEL_NAME_PATTER = Pattern.compile("[a-zA-Z_][a-zA-Z0-9_]*"); + + private String name; + private String value; + + 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()); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public static boolean hasValidName(String label) { + return LABEL_NAME_PATTER.matcher(label).matches(); + } +} diff --git a/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/LabelFactory.java b/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/LabelFactory.java new file mode 100644 index 0000000..91aa0f5 --- /dev/null +++ b/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/LabelFactory.java @@ -0,0 +1,149 @@ +package pl.tkowalcz.tjahzi.reload4j; + +import org.apache.log4j.helpers.LogLog; +import pl.tkowalcz.tjahzi.github.GitHubDocs; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.util.Arrays.stream; +import static java.util.stream.Collectors.counting; + +public class LabelFactory { + + private final String logLevelLabel; + private final String loggerNameLabel; + private final String threadNameLabel; + private final Label[] labels; + + public LabelFactory( + String logLevelLabel, + String loggerNameLabel, + String threadNameLabel, + Label... labels + ) { + this.logLevelLabel = logLevelLabel; + this.loggerNameLabel = loggerNameLabel; + this.threadNameLabel = threadNameLabel; + this.labels = labels; + } + + public HashMap convertLabelsDroppingInvalid() { + detectAndLogDuplicateLabels(); + return convertAndLogViolations(); + } + + public String validateLogLevelLabel(HashMap existingLabels) { + if (logLevelLabel != null) { + return validatePredefinedLabelAgainst( + existingLabels, + logLevelLabel, + "log level label" + ); + } + + return null; + } + + public String validateLoggerNameLabel(HashMap existingLabels) { + if (loggerNameLabel != null) { + return validatePredefinedLabelAgainst( + existingLabels, + loggerNameLabel, + "logger name label" + ); + } + + return null; + } + + public String validateThreadNameLabel(HashMap existingLabels) { + if (threadNameLabel != null) { + return validatePredefinedLabelAgainst( + existingLabels, + threadNameLabel, + "thread name label" + ); + } + + return null; + } + + private void detectAndLogDuplicateLabels() { + List duplicatedLabels = stream(labels) + .collect(Collectors.groupingBy(Label::getName, counting())) + .entrySet() + .stream().filter(entry -> entry.getValue() > 1) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + + if (!duplicatedLabels.isEmpty()) { + LogLog.warn( + String.format( + "There are duplicated labels which is not allowed by Loki. " + + "These labels will be deduplicated non-deterministically: %s\n", + duplicatedLabels + ) + ); + } + } + + private HashMap convertAndLogViolations() { + HashMap lokiLabels = new HashMap<>(); + + stream(labels) + .flatMap(label -> { + if (label.hasValidName()) { + return Stream.of(label); + } + + LogLog.warn( + String.format( + "Ignoring label '%s' - contains invalid characters. %s\n", + label.getName(), + GitHubDocs.LABEL_NAMING.getLogMessage() + ) + ); + + return Stream.of(); + } + ) + .forEach(__ -> lokiLabels.put(__.getName(), __.getValue())); + + return lokiLabels; + } + + private String validatePredefinedLabelAgainst( + Map existingLabels, + String predefinedLabel, + String predefinedLabelDescription + ) { + if (!Label.hasValidName(predefinedLabel)) { + LogLog.warn( + String.format( + "Ignoring %s '%s' - contains invalid characters. %s\n", + predefinedLabelDescription, + predefinedLabel, + GitHubDocs.LABEL_NAMING.getLogMessage() + ) + ); + + return null; + } + + if (existingLabels.remove(predefinedLabel) != null) { + LogLog.warn( + String.format( + "Ignoring %s '%s' - conflicts with label defined in configuration.\n", + predefinedLabelDescription, + predefinedLabel + ) + ); + } + + return predefinedLabel; + } +} diff --git a/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/LokiAppender.java b/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/LokiAppender.java new file mode 100644 index 0000000..8e03de6 --- /dev/null +++ b/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/LokiAppender.java @@ -0,0 +1,118 @@ +package pl.tkowalcz.tjahzi.reload4j; + +import org.apache.log4j.helpers.LogLog; +import org.apache.log4j.spi.LoggingEvent; +import pl.tkowalcz.tjahzi.LabelSerializer; +import pl.tkowalcz.tjahzi.LabelSerializers; +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.List; +import java.util.concurrent.TimeUnit; + +public class LokiAppender extends LokiAppenderConfigurator { + + private LoggingSystem loggingSystem; + private TjahziLogger logger; + + private String logLevelLabel; + private String loggerNameLabel; + private String threadNameLabel; + private List mdcLogLabels; + + private MutableMonitoringModuleWrapper monitoringModuleWrapper; + + /** + * 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 + public void activateOptions() { + LokiAppenderFactory lokiAppenderFactory = new LokiAppenderFactory(this); + loggingSystem = lokiAppenderFactory.createAppender(); + logLevelLabel = lokiAppenderFactory.getLogLevelLabel(); + loggerNameLabel = lokiAppenderFactory.getLoggerNameLabel(); + threadNameLabel = lokiAppenderFactory.getThreadNameLabel(); + mdcLogLabels = lokiAppenderFactory.getMdcLogLabels(); + monitoringModuleWrapper = lokiAppenderFactory.getMonitoringModuleWrapper(); + + logger = loggingSystem.createLogger(); + loggingSystem.start(); + } + + @Override + protected void append(LoggingEvent event) { + String logLevel = event.getLevel().toString(); + String loggerName = event.getLoggerName(); + String threadName = event.getThreadName(); + + String message = layout.format(event); + + LabelSerializer labelSerializer = LabelSerializers.threadLocal(); + appendLogLabel(labelSerializer, logLevel); + appendLoggerLabel(labelSerializer, loggerName); + appendThreadLabel(labelSerializer, threadName); + appendMdcLogLabels(labelSerializer, event); + + logger.log( + event.getTimeStamp(), + 0L, + labelSerializer, + ByteBuffer.wrap(message.getBytes()) + ); + } + + private void appendLogLabel(LabelSerializer labelSerializer, String logLevel) { + if (logLevelLabel != null) { + labelSerializer.appendLabel(logLevelLabel, logLevel); + } + } + + private void appendLoggerLabel(LabelSerializer labelSerializer, String loggerName) { + if (loggerNameLabel != null) { + labelSerializer.appendLabel(loggerNameLabel, loggerName); + } + } + + private void appendThreadLabel(LabelSerializer labelSerializer, String threadName) { + if (threadNameLabel != null) { + labelSerializer.appendLabel(threadNameLabel, threadName); + } + } + + @SuppressWarnings("ForLoopReplaceableByForEach") // Allocator goes brrrr + private void appendMdcLogLabels(LabelSerializer serializer, + LoggingEvent mdcPropertyMap) { + for (int i = 0; i < mdcLogLabels.size(); i++) { + String mdcLogLabel = mdcLogLabels.get(i); + + Object mdcValue = mdcPropertyMap.getMDC(mdcLogLabel); + if (mdcValue != null) { + serializer.appendLabel(mdcLogLabel, mdcValue.toString()); + } + } + } + + public void close() { + loggingSystem.close( + (int) TimeUnit.SECONDS.toMillis(getShutdownTimeoutSeconds()), + thread -> LogLog.error("Loki appender was unable to stop thread on shutdown: " + thread) + ); + } + + public boolean requiresLayout() { + return true; + } +} diff --git a/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/LokiAppenderConfigurator.java b/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/LokiAppenderConfigurator.java new file mode 100644 index 0000000..f09a670 --- /dev/null +++ b/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/LokiAppenderConfigurator.java @@ -0,0 +1,239 @@ +package pl.tkowalcz.tjahzi.reload4j; + +import org.apache.log4j.AppenderSkeleton; + +import java.util.ArrayList; +import java.util.List; + +public abstract class LokiAppenderConfigurator extends AppenderSkeleton { + + static final int BYTES_IN_MEGABYTE = 1024 * 1024; + + private String url; + private String logEndpoint; + + private String host; + private int port; + + private boolean useSSL; + + private boolean useDaemonThreads; + + private String username; + + private String password; + + private int connectTimeoutMillis = 5000; + private int readTimeoutMillis = 60_000; + private int maxRetries = 3; + + private int bufferSizeMegabytes = 32; + private boolean useOffHeapBuffer = true; + + private String logLevelLabel; + private String loggerNameLabel; + private String threadNameLabel; + private final List mdcLogLabels = new ArrayList<>(); + + private long batchSize = 10_2400; + private long batchWait = 5; + private long shutdownTimeoutSeconds = 10; + private long logShipperWakeupIntervalMillis = 10; + + private int maxRequestsInFlight = 100; + + private String headers; + private String labels; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getLogEndpoint() { + return logEndpoint; + } + + public void setLogEndpoint(String logEndpoint) { + this.logEndpoint = logEndpoint; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public boolean isUseSSL() { + return useSSL; + } + + public void setUseSSL(boolean useSSL) { + this.useSSL = useSSL; + } + + public boolean isUseDaemonThreads() { + return useDaemonThreads; + } + + public void setUseDaemonThreads(boolean useDaemonThreads) { + this.useDaemonThreads = useDaemonThreads; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getConnectTimeoutMillis() { + return connectTimeoutMillis; + } + + public void setConnectTimeoutMillis(int connectTimeoutMillis) { + this.connectTimeoutMillis = connectTimeoutMillis; + } + + public int getReadTimeoutMillis() { + return readTimeoutMillis; + } + + public void setReadTimeoutMillis(int readTimeoutMillis) { + this.readTimeoutMillis = readTimeoutMillis; + } + + public int getMaxRetries() { + return maxRetries; + } + + public void setMaxRetries(int maxRetries) { + this.maxRetries = maxRetries; + } + + public int getBufferSizeMegabytes() { + return bufferSizeMegabytes; + } + + public void setBufferSizeMegabytes(int bufferSizeMegabytes) { + this.bufferSizeMegabytes = bufferSizeMegabytes; + } + + public boolean isUseOffHeapBuffer() { + return useOffHeapBuffer; + } + + public void setUseOffHeapBuffer(boolean useOffHeapBuffer) { + this.useOffHeapBuffer = useOffHeapBuffer; + } + + public String getLogLevelLabel() { + return logLevelLabel; + } + + public void setLogLevelLabel(String logLevelLabel) { + this.logLevelLabel = logLevelLabel; + } + + public String getLoggerNameLabel() { + return loggerNameLabel; + } + + public void setLoggerNameLabel(String loggerNameLabel) { + this.loggerNameLabel = loggerNameLabel; + } + + public String getThreadNameLabel() { + return threadNameLabel; + } + + public void setThreadNameLabel(String threadNameLabel) { + this.threadNameLabel = threadNameLabel; + } + + public void setBatchSize(long batchSize) { + this.batchSize = batchSize; + } + + public void setBatchWait(long batchWait) { + this.batchWait = batchWait; + } + + public long getBatchSize() { + return batchSize; + } + + public long getBatchWait() { + return batchWait; + } + + public long getShutdownTimeoutSeconds() { + return shutdownTimeoutSeconds; + } + + public void setShutdownTimeoutSeconds(long shutdownTimeoutSeconds) { + this.shutdownTimeoutSeconds = shutdownTimeoutSeconds; + } + + public long getLogShipperWakeupIntervalMillis() { + return logShipperWakeupIntervalMillis; + } + + public void setLogShipperWakeupIntervalMillis(long logShipperWakeupIntervalMillis) { + this.logShipperWakeupIntervalMillis = logShipperWakeupIntervalMillis; + } + + public int getMaxRequestsInFlight() { + return maxRequestsInFlight; + } + + public void setMaxRequestsInFlight(int maxRequestsInFlight) { + this.maxRequestsInFlight = maxRequestsInFlight; + } + + public List getMdcLogLabels() { + return mdcLogLabels; + } + + public void addMdcLogLabel(String mdcLogLabel) { + this.mdcLogLabels.add(mdcLogLabel); + } + + public String getHeaders() { + return headers; + } + + public void setHeaders(String headers) { + this.headers = headers; + } + + public String getLabels() { + return labels; + } + + public void setLabels(String labels) { + this.labels = labels; + } +} diff --git a/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/LokiAppenderFactory.java b/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/LokiAppenderFactory.java new file mode 100644 index 0000000..8fdf8a8 --- /dev/null +++ b/reload4j-appender/src/main/java/pl/tkowalcz/tjahzi/reload4j/LokiAppenderFactory.java @@ -0,0 +1,122 @@ +package pl.tkowalcz.tjahzi.reload4j; + +import org.apache.log4j.helpers.LogLog; +import pl.tkowalcz.tjahzi.LoggingSystem; +import pl.tkowalcz.tjahzi.TjahziInitializer; +import pl.tkowalcz.tjahzi.github.GitHubDocs; +import pl.tkowalcz.tjahzi.http.ClientConfiguration; +import pl.tkowalcz.tjahzi.http.HttpClientFactory; +import pl.tkowalcz.tjahzi.http.NettyHttpClient; +import pl.tkowalcz.tjahzi.stats.MutableMonitoringModuleWrapper; +import pl.tkowalcz.tjahzi.stats.StandardMonitoringModule; + +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; + +public class LokiAppenderFactory { + + private final LokiAppenderConfigurator configurator; + + private final HashMap lokiLabels; + private final String logLevelLabel; + private final String loggerNameLabel; + private final String threadNameLabel; + private final List mdcLogLabels; + private final MutableMonitoringModuleWrapper monitoringModuleWrapper; + + public LokiAppenderFactory(LokiAppenderConfigurator configurator) { + this.configurator = configurator; + + LabelFactory labelFactory = new LabelFactory( + configurator.getLogLevelLabel(), + configurator.getLoggerNameLabel(), + configurator.getThreadNameLabel(), + CommaDelimitedListParser + .parseString(configurator.getLabels(), Label::createLabel) + .toArray(Label[]::new) + ); + + lokiLabels = labelFactory.convertLabelsDroppingInvalid(); + logLevelLabel = labelFactory.validateLogLevelLabel(lokiLabels); + loggerNameLabel = labelFactory.validateLoggerNameLabel(lokiLabels); + threadNameLabel = labelFactory.validateThreadNameLabel(lokiLabels); + mdcLogLabels = configurator.getMdcLogLabels(); + monitoringModuleWrapper = new MutableMonitoringModuleWrapper(); + } + + public LoggingSystem createAppender() { + ClientConfiguration configurationBuilder = ClientConfiguration.builder() + .withUrl(configurator.getUrl()) + .withLogEndpoint(configurator.getLogEndpoint()) + .withHost(configurator.getHost()) + .withPort(configurator.getPort()) + .withUseSSL(configurator.isUseSSL()) + .withUsername(configurator.getUsername()) + .withPassword(configurator.getPassword()) + .withConnectionTimeoutMillis(configurator.getConnectTimeoutMillis()) + .withMaxRetries(configurator.getMaxRetries()) + .withRequestTimeoutMillis(configurator.getReadTimeoutMillis()) + .withMaxRequestsInFlight(configurator.getMaxRequestsInFlight()) + .build(); + + String[] additionalHeaders = CommaDelimitedListParser.parseString(configurator.getHeaders(), Header::new).stream() + .flatMap(header -> Stream.of(header.getName(), header.getValue())) + .toArray(String[]::new); + + monitoringModuleWrapper.setMonitoringModule(new StandardMonitoringModule()); + + NettyHttpClient httpClient = HttpClientFactory + .defaultFactory() + .getHttpClient( + configurationBuilder, + monitoringModuleWrapper, + additionalHeaders + ); + + int bufferSizeBytes = configurator.getBufferSizeMegabytes() * LokiAppenderConfigurator.BYTES_IN_MEGABYTE; + if (!TjahziInitializer.isCorrectSize(bufferSizeBytes)) { + LogLog.warn( + String.format( + "Invalid log buffer size %d - using nearest power of two greater than provided value, no less than 1MB. %s\n", + bufferSizeBytes, + GitHubDocs.LOG_BUFFER_SIZING.getLogMessage() + ) + ); + } + + return new TjahziInitializer().createLoggingSystem( + httpClient, + monitoringModuleWrapper, + lokiLabels, + configurator.getBatchSize(), + TimeUnit.SECONDS.toMillis(configurator.getBatchWait()), + bufferSizeBytes, + configurator.getLogShipperWakeupIntervalMillis(), + TimeUnit.SECONDS.toMillis(configurator.getShutdownTimeoutSeconds()), + configurator.isUseOffHeapBuffer(), + configurator.isUseDaemonThreads() + ); + } + + public String getLogLevelLabel() { + return logLevelLabel; + } + + public String getLoggerNameLabel() { + return loggerNameLabel; + } + + public String getThreadNameLabel() { + return threadNameLabel; + } + + public MutableMonitoringModuleWrapper getMonitoringModuleWrapper() { + return monitoringModuleWrapper; + } + + public List getMdcLogLabels() { + return mdcLogLabels; + } +} diff --git a/reload4j-appender/src/test/java/pl/tkowalcz/tjahzi/reload4j/CommaDelimitedListParserTest.java b/reload4j-appender/src/test/java/pl/tkowalcz/tjahzi/reload4j/CommaDelimitedListParserTest.java new file mode 100644 index 0000000..b616c40 --- /dev/null +++ b/reload4j-appender/src/test/java/pl/tkowalcz/tjahzi/reload4j/CommaDelimitedListParserTest.java @@ -0,0 +1,107 @@ +package pl.tkowalcz.tjahzi.reload4j; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class CommaDelimitedListParserTest { + + @Test + public void testHappyPathScenario() { + String input = "server-address:10.1.1.2,organisation:foo,namespace:bar"; + Label[] labels = CommaDelimitedListParser.parseString(input, Label::createLabel).toArray(new Label[0]); + + assertEquals(3, labels.length); + assertEquals("server-address", labels[0].getName()); + assertEquals("10.1.1.2", labels[0].getValue()); + assertEquals("organisation", labels[1].getName()); + assertEquals("foo", labels[1].getValue()); + assertEquals("namespace", labels[2].getName()); + assertEquals("bar", labels[2].getValue()); + } + + @Test + public void testEmptyStringScenario() { + String input = ""; + List