mirror of
https://github.com/tiennm99/tjahzi.git
synced 2026-10-15 22:15:14 +00:00
Merge pull request #62 from tkowalcz/56-patterns-in-labels
56 patterns in labels
This commit is contained in:
19 files changed
+466
-57
No files matched your search
@@ -32,19 +32,25 @@ public class LabelSerializer {
|
||||
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) {
|
||||
|
||||
@@ -64,20 +64,19 @@ class LabelSerializerTest {
|
||||
// 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);
|
||||
|
||||
@@ -30,6 +30,7 @@ Log4j2 appender seemed like a good first.
|
||||
1. Include minimal appender configuration:
|
||||
|
||||
```xml
|
||||
|
||||
<Loki name="loki-appender">
|
||||
<host>${sys:loki.host}</host>
|
||||
<port>${sys:loki.port}</port>
|
||||
@@ -45,6 +46,7 @@ Log4j2 appender seemed like a good first.
|
||||
1. Reference the appender from inside one of your logger definitions:
|
||||
|
||||
```xml
|
||||
|
||||
<Root level="INFO">
|
||||
<AppenderRef ref="Loki"/>
|
||||
</Root>
|
||||
@@ -59,6 +61,7 @@ will configure the appender to call to URL: `http://loki.mydomain.com:3100/loki/
|
||||
### Grafana Cloud configuration
|
||||
|
||||
Tjahzi can send logs to Grafana Cloud. It needs two things to be configured:
|
||||
|
||||
- Set port number to `443` which switches HTTP client into HTTPS mode.
|
||||
- Specify `username` and `password` for HTTP basic authentication that Grafana uses.
|
||||
|
||||
@@ -66,6 +69,7 @@ Password is your "Grafana.com API Key" and can be generated in "Grafana datasour
|
||||
is just for illustrative purposes.
|
||||
|
||||
```xml
|
||||
|
||||
<Loki name="loki-appender">
|
||||
<!-- example host -->
|
||||
<host>logs-prod-us-central1.grafana.net</host>
|
||||
@@ -115,12 +119,27 @@ attribute of configuration so that the appender can be found.
|
||||
Contents of the properties
|
||||
are [automatically interpolated by Log4j2](https://logging.apache.org/log4j/log4j-2.2/manual/configuration.html#PropertySubstitution)
|
||||
. All environment, system etc. variable references will be replaced by their values during initialization of the
|
||||
appender. The exception is context/MDC (`${ctx:foo}`) value lookup as it make sense only during runtime.
|
||||
appender. The exception to this rule is context/MDC (`${ctx:foo}`) value lookup - it is performed for each message at
|
||||
runtime (allocation free).
|
||||
|
||||
NOTE: This process could have been executed for every lookup type at runtime (for each log message). This approach was
|
||||
deemed too expensive. If you need a mechanism to replace a variable (other than context/MDC) after logging system
|
||||
initialization I would love to hear your use case - please file an issue.
|
||||
|
||||
## Patterns in Labels
|
||||
|
||||
Alternative way of specifying label contents is via pattern attribute:
|
||||
|
||||
```xml
|
||||
|
||||
<Label name="server" pattern="%C{1.}"/>
|
||||
```
|
||||
|
||||
This pattern is compatible with
|
||||
Log4j [pattern layout](https://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout). In fact, we reuse lgo4j
|
||||
internal classes for this implementation. It is generally efficient and allocation free as
|
||||
per [documentation](https://logging.apache.org/log4j/log4j-2.12.1/manual/garbagefree.html#PatternLayout).
|
||||
|
||||
## Details
|
||||
|
||||
Let's go through the example config above and analyze configuration options (**Note: Tags are case-insensitive**).
|
||||
@@ -154,6 +173,13 @@ when [running Loki in multi-tenant mode](https://grafana.com/docs/loki/latest/op
|
||||
Specify additional labels attached to each log line sent via this appender instance. See also note
|
||||
about [label naming](https://github.com/tkowalcz/tjahzi/wiki/Label-naming).
|
||||
|
||||
You can use value attribute to specify static text. You can use `${}` variable substitution inside that text and Tjahzi
|
||||
will resolve variables once at startup. If the varaible is a context/MDC lookup it will be resolved dynamically for each
|
||||
log line.
|
||||
|
||||
This tag also supports `pattern` attribute where you can use pattern layout expressions that will be resolved at
|
||||
runtime.
|
||||
|
||||
#### LogLevelLabel (optional)
|
||||
|
||||
If defined then log level label of configured name will be added to each line sent to Loki. It will contain Log4j log
|
||||
|
||||
@@ -122,7 +122,12 @@ public class LokiAppenderBuilder<B extends LokiAppenderBuilder<B>> extends Abstr
|
||||
);
|
||||
}
|
||||
|
||||
LabelFactory labelFactory = new LabelFactory(logLevelLabel, labels);
|
||||
LabelFactory labelFactory = new LabelFactory(
|
||||
getConfiguration(),
|
||||
logLevelLabel,
|
||||
labels
|
||||
);
|
||||
|
||||
LabelsDescriptor labelsDescriptor = labelFactory.convertLabelsDroppingInvalid();
|
||||
logLevelLabel = labelsDescriptor.getLogLevelLabel();
|
||||
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ public class AggregateLabelPrinter implements LabelPrinter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append(LogEvent event, Consumer<String> appendable) {
|
||||
public void append(LogEvent event, Consumer<CharSequence> appendable) {
|
||||
for (LabelPrinter node : nodes) {
|
||||
node.append(event, appendable);
|
||||
}
|
||||
|
||||
@@ -7,37 +7,69 @@ import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
|
||||
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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -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 @@ public class LabelFactory {
|
||||
)
|
||||
.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<String, ?> staticLabels,
|
||||
|
||||
@@ -7,7 +7,7 @@ import java.util.function.Consumer;
|
||||
|
||||
public interface LabelPrinter {
|
||||
|
||||
void append(LogEvent event, Consumer<String> appendable);
|
||||
void append(LogEvent event, Consumer<CharSequence> appendable);
|
||||
|
||||
default boolean isStatic() {
|
||||
return false;
|
||||
|
||||
@@ -22,7 +22,7 @@ public class Literal implements LabelPrinter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append(LogEvent event, Consumer<String> appendable) {
|
||||
public void append(LogEvent event, Consumer<CharSequence> appendable) {
|
||||
appendable.accept(contents);
|
||||
}
|
||||
}
|
||||
+53
@@ -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<PatternFormatter> formatters;
|
||||
|
||||
public Log4jAdapterLabelPrinter(List<PatternFormatter> formatters) {
|
||||
this.formatters = formatters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append(LogEvent event, Consumer<CharSequence> 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<PatternFormatter> parse) {
|
||||
return new Log4jAdapterLabelPrinter(parse);
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ public class MDCLookup implements LabelPrinter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append(LogEvent event, Consumer<String> appendable) {
|
||||
public void append(LogEvent event, Consumer<CharSequence> appendable) {
|
||||
Object value = event.getContextData().getValue(variableName);
|
||||
if (value != null) {
|
||||
appendable.accept(value.toString());
|
||||
|
||||
+37
-19
@@ -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 @@ class LabelFactoryTest {
|
||||
@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 @@ class LabelFactoryTest {
|
||||
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 @@ class LabelFactoryTest {
|
||||
@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 @@ class LabelFactoryTest {
|
||||
// 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 @@ class LabelFactoryTest {
|
||||
// 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 @@ class LabelFactoryTest {
|
||||
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 @@ class LabelFactoryTest {
|
||||
public Map.Entry<String, String> asEntry(Label label) {
|
||||
return Map.entry(label.getName(), label.getValue());
|
||||
}
|
||||
|
||||
public Map.Entry<String, String> asEntryUsingPattern(Label label) {
|
||||
return Map.entry(label.getName(), label.getPattern());
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
+2
-2
@@ -55,7 +55,7 @@ class LabelsContextSubstitutionTest {
|
||||
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 @@ class LabelsContextSubstitutionTest {
|
||||
.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",
|
||||
|
||||
+124
@@ -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<PatternFormatter> patternFormatters = patternParser.parse("%mdc{tid} %5p %c{1} - %m");
|
||||
|
||||
Log4jAdapterLabelPrinter printer = new Log4jAdapterLabelPrinter(patternFormatters);
|
||||
|
||||
Map<String, String> 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<PatternFormatter> 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<Arguments> 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<PatternFormatter> patternFormatters = patternParser.parse(pattern);
|
||||
Log4jAdapterLabelPrinter labelPrinter = Log4jAdapterLabelPrinter.of(patternFormatters);
|
||||
|
||||
// When
|
||||
boolean actual = labelPrinter.isStatic();
|
||||
|
||||
// Then
|
||||
assertThat(actual).isEqualTo(isStatic);
|
||||
}
|
||||
}
|
||||
+102
@@ -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"))
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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());
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
</PatternLayout>
|
||||
|
||||
<Header name="server" value="127.0.0.1"/>
|
||||
|
||||
<Label name="server" value="127.0.0.1"/>
|
||||
<Label name="server" value="127.0.0.1"/>
|
||||
<Label name="object" value="prefix_${ctx:object}"/>
|
||||
<Label name="owner" value="${ctx:owner}_suffix"/>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration packages="pl.tkowalcz.tjahzi.log4j2">
|
||||
<Loggers>
|
||||
<Root level="INFO">
|
||||
<AppenderRef ref="Loki"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
|
||||
<appenders>
|
||||
<Loki name="Loki">
|
||||
<host>${sys:loki.host}</host>
|
||||
<port>${sys:loki.port}</port>
|
||||
|
||||
<ThresholdFilter level="ALL"/>
|
||||
<PatternLayout>
|
||||
<Pattern>%c{1} - %m</Pattern>
|
||||
</PatternLayout>
|
||||
|
||||
<Header name="server" value="127.0.0.1"/>
|
||||
|
||||
<Label name="server" pattern="127.0.0.1"/>
|
||||
<Label name="class_pattern" pattern="%C{1.}"/>
|
||||
<Label name="sequence_number" pattern="%sn"/>
|
||||
<Label name="mdc_tid" pattern="%mdc{tid}"/>
|
||||
</Loki>
|
||||
</appenders>
|
||||
</configuration>
|
||||
Reference in new issue
Block a user