From f321d6a718eaa2a87e40d1c54a399379cf0932b9 Mon Sep 17 00:00:00 2001 From: "tomasz.kowalczewski" Date: Sun, 4 Jul 2021 23:07:03 +0200 Subject: [PATCH 1/2] Re #56: Added pattern formatting for Labels based on Log4j patterns. --- .../pl/tkowalcz/tjahzi/LabelSerializer.java | 14 +- .../tkowalcz/tjahzi/LabelSerializerTest.java | 27 ++-- .../tjahzi/log4j2/LokiAppenderBuilder.java | 7 +- .../log4j2/labels/AggregateLabelPrinter.java | 2 +- .../tkowalcz/tjahzi/log4j2/labels/Label.java | 44 ++++++- .../tjahzi/log4j2/labels/LabelFactory.java | 19 ++- .../tjahzi/log4j2/labels/LabelPrinter.java | 2 +- .../tjahzi/log4j2/labels/Literal.java | 2 +- .../labels/Log4jAdapterLabelPrinter.java | 53 ++++++++ .../tjahzi/log4j2/labels/MDCLookup.java | 2 +- .../log4j2/labels/LabelFactoryTest.java | 56 +++++--- .../tjahzi/log4j2/labels/LabelTest.java | 6 +- .../labels/LabelsContextSubstitutionTest.java | 4 +- .../labels/Log4jAdapterLabelPrinterTest.java | 124 ++++++++++++++++++ .../labels/Log4jPatternsInLabelsTest.java | 102 ++++++++++++++ .../LogLevelLabelConfigurationTest.java | 2 +- ...ontext-substitution-test-configuration.xml | 2 + ...log4j-patterns-in-labels-configuration.xml | 27 ++++ 18 files changed, 439 insertions(+), 56 deletions(-) create mode 100644 log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/Log4jAdapterLabelPrinter.java create mode 100644 log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/Log4jAdapterLabelPrinterTest.java create mode 100644 log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/Log4jPatternsInLabelsTest.java create mode 100644 log4j2-appender/src/test/resources/log4j-patterns-in-labels-configuration.xml diff --git a/core/src/main/java/pl/tkowalcz/tjahzi/LabelSerializer.java b/core/src/main/java/pl/tkowalcz/tjahzi/LabelSerializer.java index 46dc394..7090d60 100644 --- a/core/src/main/java/pl/tkowalcz/tjahzi/LabelSerializer.java +++ b/core/src/main/java/pl/tkowalcz/tjahzi/LabelSerializer.java @@ -32,19 +32,25 @@ public LabelSerializer appendLabelName(String key) { return this; } - public void startAppendingLabelValue() { + public LabelSerializer startAppendingLabelValue() { lastSizePosition = cursor; cursor += Integer.BYTES; + + return this; } - public void appendPartialLabelValue(String value) { - cursor += buffer.putStringWithoutLengthAscii(cursor, value); + public LabelSerializer appendPartialLabelValue(CharSequence value) { + cursor += buffer.putStringWithoutLengthAscii(cursor, value.toString()); + + return this; } - public void finishAppendingLabelValue() { + public LabelSerializer finishAppendingLabelValue() { buffer.putInt( lastSizePosition, cursor - lastSizePosition - Integer.BYTES); + + return this; } public LabelSerializer appendLabel(String key, String value) { diff --git a/core/src/test/java/pl/tkowalcz/tjahzi/LabelSerializerTest.java b/core/src/test/java/pl/tkowalcz/tjahzi/LabelSerializerTest.java index 60f8e50..302cae1 100644 --- a/core/src/test/java/pl/tkowalcz/tjahzi/LabelSerializerTest.java +++ b/core/src/test/java/pl/tkowalcz/tjahzi/LabelSerializerTest.java @@ -64,20 +64,19 @@ void shouldAppendMultipartLabelsMixedWithRegular() { // When serializer.appendLabel("Foo", "Bar"); - serializer.appendLabelName("Aliens"); - serializer.startAppendingLabelValue(); - serializer.appendPartialLabelValue("Kang"); - serializer.appendPartialLabelValue("Kodos"); - serializer.appendPartialLabelValue("Johnson"); - serializer.finishAppendingLabelValue(); - - serializer.appendLabelName("Omicronians"); - serializer.startAppendingLabelValue(); - serializer.appendPartialLabelValue("Lrrr"); - serializer.appendPartialLabelValue("RULER OF THE PLANET OMICRON PERSEI EIGHT"); - serializer.finishAppendingLabelValue(); - - serializer.appendLabel("Popplers", "Problem"); + serializer + .appendLabelName("Aliens") + .startAppendingLabelValue() + .appendPartialLabelValue("Kang") + .appendPartialLabelValue("Kodos") + .appendPartialLabelValue("Johnson") + .finishAppendingLabelValue() + .appendLabelName("Omicronians") + .startAppendingLabelValue() + .appendPartialLabelValue("Lrrr") + .appendPartialLabelValue("RULER OF THE PLANET OMICRON PERSEI EIGHT") + .finishAppendingLabelValue() + .appendLabel("Popplers", "Problem"); // Then assertThat(serializer.getLabelsCount()).isEqualTo(4); 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 9e8bf6e..de675ba 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 @@ -118,7 +118,12 @@ public LokiAppender build() { ); } - LabelFactory labelFactory = new LabelFactory(logLevelLabel, labels); + LabelFactory labelFactory = new LabelFactory( + getConfiguration(), + logLevelLabel, + labels + ); + LabelsDescriptor labelsDescriptor = labelFactory.convertLabelsDroppingInvalid(); logLevelLabel = labelsDescriptor.getLogLevelLabel(); diff --git a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/AggregateLabelPrinter.java b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/AggregateLabelPrinter.java index cedeb83..07b3ac2 100644 --- a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/AggregateLabelPrinter.java +++ b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/AggregateLabelPrinter.java @@ -18,7 +18,7 @@ public static LabelPrinter of(List result) { } @Override - public void append(LogEvent event, Consumer appendable) { + public void append(LogEvent event, Consumer appendable) { for (LabelPrinter node : nodes) { node.append(event, appendable); } diff --git a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/Label.java b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/Label.java index ea53072..1578029 100644 --- a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/Label.java +++ b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/Label.java @@ -7,37 +7,69 @@ import org.apache.logging.log4j.core.config.plugins.PluginFactory; import org.apache.logging.log4j.core.config.plugins.PluginValue; import org.apache.logging.log4j.status.StatusLogger; -import pl.tkowalcz.tjahzi.log4j2.Property; import java.util.regex.Pattern; @Plugin(name = "label", category = Node.CATEGORY, printObject = true) -public class Label extends Property { +public class Label { private static final Logger LOGGER = StatusLogger.getLogger(); private static final Pattern LABEL_NAME_PATTER = Pattern.compile("[a-zA-Z_][a-zA-Z0-9_]*"); - private Label(String name, String value) { - super(name, value); + private final String name; + private final String value; + private final String pattern; + + private Label(String name, String value, String pattern) { + this.name = name; + this.value = value; + this.pattern = pattern; } @PluginFactory public static Label createLabel( @PluginAttribute("name") String name, - @PluginValue("value") String value) { + @PluginValue("value") String value, + @PluginValue("pattern") String pattern + ) { if (name == null) { LOGGER.error("Property name cannot be null"); } - return new Label(name, value); + if (pattern == null && value == null) { + LOGGER.error("Property must have pattern or value specified"); + } + + return new Label(name, value, pattern); } public boolean hasValidName() { return hasValidName(getName()); } + public String getName() { + return name; + } + + public String getPattern() { + return pattern; + } + + public String getValue() { + return value; + } + public static boolean hasValidName(String label) { return LABEL_NAME_PATTER.matcher(label).matches(); } + + @Override + public String toString() { + return "Label{" + + "name='" + name + '\'' + + ", pattern='" + pattern + '\'' + + ", value='" + value + '\'' + + '}'; + } } diff --git a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelFactory.java b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelFactory.java index 6daaed1..c8929b9 100644 --- a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelFactory.java +++ b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelFactory.java @@ -1,6 +1,9 @@ package pl.tkowalcz.tjahzi.log4j2.labels; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.layout.PatternLayout; +import org.apache.logging.log4j.core.pattern.PatternParser; import org.apache.logging.log4j.status.StatusLogger; import pl.tkowalcz.tjahzi.github.GitHubDocs; @@ -22,9 +25,13 @@ public class LabelFactory { private final String logLevelLabel; private final Label[] labels; - public LabelFactory(String logLevelLabel, Label... labels) { + private final PatternParser patternParser; + + public LabelFactory(Configuration configuration, String logLevelLabel, Label... labels) { this.logLevelLabel = logLevelLabel; this.labels = labels; + + this.patternParser = new PatternParser(configuration, PatternLayout.KEY, null); } public LabelsDescriptor convertLabelsDroppingInvalid() { @@ -95,11 +102,19 @@ private Map convertAndLogViolations() { ) .collect(toMap( Label::getName, - LabelPrinterFactory::parse, + this::toLabelOrLog4jPattern, (original, duplicate) -> duplicate) ); } + private LabelPrinter toLabelOrLog4jPattern(Label label) { + if (label.getPattern() != null) { + return Log4jAdapterLabelPrinter.of(patternParser.parse(label.getPattern())); + } + + return LabelPrinterFactory.parse(label); + } + private static String validateLogLevelLabel( String logLevelLabel, Map staticLabels, diff --git a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelPrinter.java b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelPrinter.java index 49cf9db..8e9339b 100644 --- a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelPrinter.java +++ b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelPrinter.java @@ -7,7 +7,7 @@ public interface LabelPrinter { - void append(LogEvent event, Consumer appendable); + void append(LogEvent event, Consumer appendable); default boolean isStatic() { return false; diff --git a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/Literal.java b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/Literal.java index d2fb396..5531ed1 100644 --- a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/Literal.java +++ b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/Literal.java @@ -22,7 +22,7 @@ public static Literal of(String string) { } @Override - public void append(LogEvent event, Consumer appendable) { + public void append(LogEvent event, Consumer appendable) { appendable.accept(contents); } } diff --git a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/Log4jAdapterLabelPrinter.java b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/Log4jAdapterLabelPrinter.java new file mode 100644 index 0000000..db51f9e --- /dev/null +++ b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/Log4jAdapterLabelPrinter.java @@ -0,0 +1,53 @@ +package pl.tkowalcz.tjahzi.log4j2.labels; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.pattern.LiteralPatternConverter; +import org.apache.logging.log4j.core.pattern.LogEventPatternConverter; +import org.apache.logging.log4j.core.pattern.PatternFormatter; + +import java.util.List; +import java.util.function.Consumer; + +@SuppressWarnings("ForLoopReplaceableByForEach") +public class Log4jAdapterLabelPrinter implements LabelPrinter { + + private final StringBuilder outputBuffer = new StringBuilder(); + private final List formatters; + + public Log4jAdapterLabelPrinter(List formatters) { + this.formatters = formatters; + } + + @Override + public void append(LogEvent event, Consumer appendable) { + outputBuffer.setLength(0); + + for (int i = 0; i < formatters.size(); i++) { + formatters.get(i).format(event, outputBuffer); + } + + appendable.accept(outputBuffer); + } + + @Override + public boolean isStatic() { + for (int i = 0; i < formatters.size(); i++) { + LogEventPatternConverter converter = formatters.get(i).getConverter(); + + if (!(converter instanceof LiteralPatternConverter)) { + return false; + } + + if (((LiteralPatternConverter) converter).getLiteral().contains("$")) { + return false; + } + + } + + return true; + } + + public static Log4jAdapterLabelPrinter of(List parse) { + return new Log4jAdapterLabelPrinter(parse); + } +} diff --git a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/MDCLookup.java b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/MDCLookup.java index 9983256..93c806e 100644 --- a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/MDCLookup.java +++ b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/MDCLookup.java @@ -19,7 +19,7 @@ public static MDCLookup of(String variableName, String defaultValue) { } @Override - public void append(LogEvent event, Consumer appendable) { + public void append(LogEvent event, Consumer appendable) { Object value = event.getContextData().getValue(variableName); if (value != null) { appendable.accept(value.toString()); diff --git a/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelFactoryTest.java b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelFactoryTest.java index 63e8d41..ec79a08 100644 --- a/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelFactoryTest.java +++ b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelFactoryTest.java @@ -1,6 +1,9 @@ package pl.tkowalcz.tjahzi.log4j2.labels; +import org.apache.logging.log4j.core.config.NullConfiguration; +import org.apache.logging.log4j.core.config.xml.XmlConfiguration; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import java.util.Map; @@ -11,11 +14,12 @@ 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"); + Label thisIsADuplicateAndShouldBeDropped = Label.createLabel("ip", "127.0.0.1", null); + Label thisShouldStay = Label.createLabel("ip", "10.0.2.34", null); + Label thisTooShouldStay = Label.createLabel("hostname", "hostname", null); LabelFactory labelFactory = new LabelFactory( + new NullConfiguration(), "log_level", thisIsADuplicateAndShouldBeDropped, thisShouldStay, @@ -36,17 +40,22 @@ void shouldRemoveDuplicatedLabels() { @Test void shouldIdentifyDynamicLabels() { // Given - Label thisIsAStaticLabel = Label.createLabel("ip", "127.0.0.1"); - Label thisIsAlsoAStaticLabel = Label.createLabel("region", "us-east-1"); - Label thisIsADynamicLabel = Label.createLabel("tenant", "${ctx:tenant}"); - Label thisIsAnotherDynamicLabel = Label.createLabel("tenant", "foo_${ctx:baz}_bar"); + Label thisIsAStaticLabel = Label.createLabel("ip", "127.0.0.1", null); + Label thisIsAlsoAStaticLabel = Label.createLabel("region", "us-east-1", null); + Label thisIsADynamicLabel = Label.createLabel("tenant", "${ctx:tenant}", null); + Label thisIsAnotherDynamicLabel = Label.createLabel("tenant", "foo_${ctx:baz}_bar", null); + Label thisIsAStaticPatternLabel = Label.createLabel("AZ", null, "us-east-1a"); + Label thisIsADynamicPatternLabel = Label.createLabel("iam_role", null, "${iam}"); LabelFactory labelFactory = new LabelFactory( + new NullConfiguration(), "log_level", thisIsAStaticLabel, thisIsAlsoAStaticLabel, thisIsADynamicLabel, - thisIsAnotherDynamicLabel + thisIsAnotherDynamicLabel, + thisIsAStaticPatternLabel, + thisIsADynamicPatternLabel ); // When @@ -56,24 +65,27 @@ void shouldIdentifyDynamicLabels() { assertThat(actual.getStaticLabels()) .containsOnly( asEntry(thisIsAStaticLabel), - asEntry(thisIsAlsoAStaticLabel) + asEntry(thisIsAlsoAStaticLabel), + asEntryUsingPattern(thisIsAStaticPatternLabel) ); assertThat(actual.getDynamicLabels().keySet()) .containsOnly( thisIsADynamicLabel.getName(), - thisIsAnotherDynamicLabel.getName() + thisIsAnotherDynamicLabel.getName(), + thisIsADynamicPatternLabel.getName() ); } @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"); + Label thisShouldStay = Label.createLabel("ip", "10.0.2.34", null); + Label thisShouldStayToo = Label.createLabel("region", "coruscant-west-2", null); + Label invalidNameShouldBeDropped = Label.createLabel("host-name", "hostname", null); LabelFactory labelFactory = new LabelFactory( + new NullConfiguration(), "log_level", thisShouldStayToo, thisShouldStay, @@ -93,10 +105,11 @@ void shouldSkipLabelsWithInvalidName() { @Test void shouldAcceptNullLogLevel() { // Given - Label label1 = Label.createLabel("ip", "10.0.2.34"); - Label label2 = Label.createLabel("region", "coruscant-west-2"); + Label label1 = Label.createLabel("ip", "10.0.2.34", null); + Label label2 = Label.createLabel("region", "coruscant-west-2", null); LabelFactory labelFactory = new LabelFactory( + new NullConfiguration(), null, label2, label1 @@ -117,10 +130,11 @@ void logLevelLabelShouldOverrideConflictingLabel() { // Given String logLevelLabel = "log_level"; - Label thisShouldStay = Label.createLabel("ip", "10.0.2.34"); - Label thisShouldBeRemovedDueToConflict = Label.createLabel(logLevelLabel, "coruscant-west-2"); + Label thisShouldStay = Label.createLabel("ip", "10.0.2.34", null); + Label thisShouldBeRemovedDueToConflict = Label.createLabel(logLevelLabel, "coruscant-west-2", null); LabelFactory labelFactory = new LabelFactory( + Mockito.mock(XmlConfiguration.class), logLevelLabel, thisShouldBeRemovedDueToConflict, thisShouldStay @@ -142,7 +156,7 @@ void shouldDisableLogLevelLabelIfItHasInvalidName() { // Given String logLevelLabel = "log-level"; - LabelFactory labelFactory = new LabelFactory(logLevelLabel); + LabelFactory labelFactory = new LabelFactory(null, logLevelLabel); // When LabelsDescriptor actual = labelFactory.convertLabelsDroppingInvalid(); @@ -155,7 +169,7 @@ void shouldDisableLogLevelLabelIfItHasInvalidName() { void shouldHandleDisabledLogLevelLabel() { // Given String logLevelLabel = null; - LabelFactory labelFactory = new LabelFactory(logLevelLabel); + LabelFactory labelFactory = new LabelFactory(null, logLevelLabel); // When LabelsDescriptor actual = labelFactory.convertLabelsDroppingInvalid(); @@ -167,4 +181,8 @@ void shouldHandleDisabledLogLevelLabel() { public Map.Entry asEntry(Label label) { return Map.entry(label.getName(), label.getValue()); } + + public Map.Entry asEntryUsingPattern(Label label) { + return Map.entry(label.getName(), label.getPattern()); + } } diff --git a/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelTest.java b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelTest.java index 294e110..68a2122 100644 --- a/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelTest.java +++ b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelTest.java @@ -9,9 +9,9 @@ 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"); + Label correctLabel = Label.createLabel("aaa_bbb_ccc", "fobar", null); + Label tooShortLabel = Label.createLabel("", "foobar", null); + Label labelWithInvalidChars = Label.createLabel("log-level", "foobar", null); // When & Then assertThat(correctLabel.hasValidName()).isTrue(); diff --git a/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelsContextSubstitutionTest.java b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelsContextSubstitutionTest.java index 020e42a..56f7f12 100644 --- a/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelsContextSubstitutionTest.java +++ b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelsContextSubstitutionTest.java @@ -55,7 +55,7 @@ void shouldSendData() throws Exception { Logger logger = LogManager.getLogger(LabelsContextSubstitutionTest.class); // When - MDC.put("object", "bust_ticket"); + MDC.put("object", "bus_ticket"); MDC.put("owner", "wally"); logger.info("Test1"); @@ -90,7 +90,7 @@ void shouldSendData() throws Exception { .body("status", equalTo("success")) .body("data.result.size()", equalTo(3)) .body("data.result.stream.server", everyItem(equalTo("127.0.0.1"))) - .body("data.result.stream.object", contains("prefix_", "prefix_bust_ticket", "prefix_comb")) + .body("data.result.stream.object", contains("prefix_", "prefix_bus_ticket", "prefix_comb")) .body("data.result.stream.owner", contains("_suffix", "wally_suffix", "jennifer_suffix")) .body("data.result.stream.default_value_test", contains("use_this_if_missing", "use_this_if_missing", "use_this_if_missing")) .body("data.result.values", diff --git a/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/Log4jAdapterLabelPrinterTest.java b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/Log4jAdapterLabelPrinterTest.java new file mode 100644 index 0000000..ea1cb51 --- /dev/null +++ b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/Log4jAdapterLabelPrinterTest.java @@ -0,0 +1,124 @@ +package pl.tkowalcz.tjahzi.log4j2.labels; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.config.NullConfiguration; +import org.apache.logging.log4j.core.impl.JdkMapAdapterStringMap; +import org.apache.logging.log4j.core.impl.MutableLogEvent; +import org.apache.logging.log4j.core.layout.PatternLayout; +import org.apache.logging.log4j.core.pattern.PatternFormatter; +import org.apache.logging.log4j.core.pattern.PatternParser; +import org.apache.logging.log4j.message.SimpleMessage; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +class Log4jAdapterLabelPrinterTest { + + @Test + void shouldHandleEmptyListOfFormatters() { + // Given + Log4jAdapterLabelPrinter printer = new Log4jAdapterLabelPrinter(List.of()); + + MutableLogEvent event = new MutableLogEvent(); + StringBuilder builder = new StringBuilder(); + + // When + printer.append(event, builder::append); + + // Then + assertThat(builder).isEmpty(); + } + + @Test + void shouldInvokeFormatters() { + // Given + PatternParser patternParser = new PatternParser(null, PatternLayout.KEY, null); + List patternFormatters = patternParser.parse("%mdc{tid} %5p %c{1} - %m"); + + Log4jAdapterLabelPrinter printer = new Log4jAdapterLabelPrinter(patternFormatters); + + Map mdcMap = Map.of( + "tid", "req-43tnfwenb" + ); + + MutableLogEvent event = new MutableLogEvent(); + event.setContextData(new JdkMapAdapterStringMap(mdcMap)); + event.setLoggerName(getClass().getName()); + event.setMessage(new SimpleMessage("Test")); + event.setLevel(Level.ERROR); + event.setLoggerFqcn(getClass().getCanonicalName()); + + StringBuilder builder = new StringBuilder(); + + // When + printer.append(event, builder::append); + + // Then + assertThat(builder.toString()).isEqualTo("req-43tnfwenb ERROR Log4jAdapterLabelPrinterTest - Test"); + } + + @Test + void shouldSubstituteEnvVariables() { + // Given + System.setProperty("server", "127.0.0.1"); + + PatternParser patternParser = new PatternParser( + new NullConfiguration(), + PatternLayout.KEY, + null + ); + List patternFormatters = patternParser.parse("${sys:server} %5p %c{1} - %m"); + + Log4jAdapterLabelPrinter printer = new Log4jAdapterLabelPrinter(patternFormatters); + + MutableLogEvent event = new MutableLogEvent(); + event.setLoggerName(getClass().getName()); + event.setMessage(new SimpleMessage("Test")); + event.setLevel(Level.ERROR); + event.setLoggerFqcn(getClass().getCanonicalName()); + + StringBuilder builder = new StringBuilder(); + + // When + printer.append(event, builder::append); + + // Then + assertThat(builder.toString()).isEqualTo("127.0.0.1 ERROR Log4jAdapterLabelPrinterTest - Test"); + } + + private static Stream shouldCorrectlyIdentifyStaticPattern() { + return Stream.of( + Arguments.of("Empty", "", true), + Arguments.of("Literal value", "literal value", true), + Arguments.of("Literal value with variable", "Literal & variable: ${server}", false), + Arguments.of("Contains pattern", "With pattern %c{1}", false) + ); + } + + @ParameterizedTest(name = "{0}") + @MethodSource + void shouldCorrectlyIdentifyStaticPattern(String name, String pattern, boolean isStatic) { + // Given + PatternParser patternParser = new PatternParser( + new NullConfiguration(), + PatternLayout.KEY, + null + ); + + List patternFormatters = patternParser.parse(pattern); + Log4jAdapterLabelPrinter labelPrinter = Log4jAdapterLabelPrinter.of(patternFormatters); + + // When + boolean actual = labelPrinter.isStatic(); + + // Then + assertThat(actual).isEqualTo(isStatic); + } +} diff --git a/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/Log4jPatternsInLabelsTest.java b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/Log4jPatternsInLabelsTest.java new file mode 100644 index 0000000..50408a4 --- /dev/null +++ b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/Log4jPatternsInLabelsTest.java @@ -0,0 +1,102 @@ +package pl.tkowalcz.tjahzi.log4j2.labels; + +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import io.restassured.parsing.Parser; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.awaitility.Awaitility; +import org.awaitility.Durations; +import org.junit.jupiter.api.Test; +import org.slf4j.MDC; +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.Matchers.contains; +import static org.hamcrest.Matchers.hasItems; +import static org.hamcrest.core.Every.everyItem; + +@Testcontainers +class Log4jPatternsInLabelsTest { + + @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("log4j-patterns-in-labels-configuration.xml") + .toURI(); + + ((org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false)) + .setConfigLocation(uri); + + Logger logger = LogManager.getLogger(Log4jPatternsInLabelsTest.class); + + // When + MDC.put("tid", "req-230rq9ubou"); + logger.info("Test1"); + + MDC.clear(); + logger.info("Test2"); + + // 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(2)) + .body("data.result.stream.server", everyItem(equalTo("127.0.0.1"))) + .body("data.result.stream.class_pattern", everyItem(equalTo("p.t.t.l.l.Log4jPatternsInLabelsTest"))) + .body("data.result.stream.sequence_number", contains("2", "1")) + .body("data.result.stream.mdc_tid", contains("", "req-230rq9ubou")) + .body("data.result.values", + hasItems( + hasItems(hasItems("Log4jPatternsInLabelsTest - Test2")), + hasItems(hasItems("Log4jPatternsInLabelsTest - Test1")) + ) + ); + }); + } +} diff --git a/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LogLevelLabelConfigurationTest.java b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LogLevelLabelConfigurationTest.java index de5e5e7..5a0a3d8 100644 --- a/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LogLevelLabelConfigurationTest.java +++ b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LogLevelLabelConfigurationTest.java @@ -37,7 +37,7 @@ class LogLevelLabelConfigurationTest { .withExposedPorts(3100); @Test - void shouldWorkWIthNoLogLevelConfigured() throws Exception { + 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/resources/labels-context-substitution-test-configuration.xml b/log4j2-appender/src/test/resources/labels-context-substitution-test-configuration.xml index 6a2bed1..7def2f3 100644 --- a/log4j2-appender/src/test/resources/labels-context-substitution-test-configuration.xml +++ b/log4j2-appender/src/test/resources/labels-context-substitution-test-configuration.xml @@ -17,6 +17,8 @@
+ +