You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by gg...@apache.org on 2014/09/15 14:34:20 UTC
git commit: [LOG4J2-428] Implement a GELF layout. Tests GZIP. Use
Jackson for JSON encoding instead of custom code.
Repository: logging-log4j2
Updated Branches:
refs/heads/master 82d43d09b -> 837e0aa18
[LOG4J2-428] Implement a GELF layout. Tests GZIP. Use Jackson for JSON
encoding instead of custom code.
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/837e0aa1
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/837e0aa1
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/837e0aa1
Branch: refs/heads/master
Commit: 837e0aa18f73f1956f7cb2f901a2fbe960501c61
Parents: 82d43d0
Author: Gary Gregory <ga...@gmail.com>
Authored: Mon Sep 15 08:34:16 2014 -0400
Committer: Gary Gregory <ga...@gmail.com>
Committed: Mon Sep 15 08:34:16 2014 -0400
----------------------------------------------------------------------
log4j-core/pom.xml | 5 +
.../logging/log4j/core/layout/GelfLayout.java | 69 +++++-----
.../log4j/core/layout/GelfLayoutTest.java | 138 ++++++++++++-------
pom.xml | 6 +
4 files changed, 136 insertions(+), 82 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/837e0aa1/log4j-core/pom.xml
----------------------------------------------------------------------
diff --git a/log4j-core/pom.xml b/log4j-core/pom.xml
index c10e8bc..88cfffd 100644
--- a/log4j-core/pom.xml
+++ b/log4j-core/pom.xml
@@ -220,6 +220,11 @@
<artifactId>json-unit</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <scope>test</scope>
+ </dependency>
<!-- Other -->
<dependency>
<groupId>commons-codec</groupId>
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/837e0aa1/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/GelfLayout.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/GelfLayout.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/GelfLayout.java
index b85cbcf..c2d9c99 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/GelfLayout.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/GelfLayout.java
@@ -37,6 +37,8 @@ import org.apache.logging.log4j.core.util.Charsets;
import org.apache.logging.log4j.core.util.KeyValuePair;
import org.apache.logging.log4j.status.StatusLogger;
+import com.fasterxml.jackson.core.io.JsonStringEncoder;
+
/**
* Lays out Graylog Extended Log Format (GELF) 1.1 log events.
* <p>
@@ -65,12 +67,12 @@ import org.apache.logging.log4j.status.StatusLogger;
public final class GelfLayout extends AbstractStringLayout {
public static enum CompressionType {
- GZIP, ZLIB, NONE
+ GZIP, NONE, ZLIB
}
- private static final byte[] EMPTY_BYTES = new byte[0];
private static final char C = ',';
private static final int COMPRESSION_THRESHOLD = 1024;
+ private static final byte[] EMPTY_BYTES = new byte[0];
private static final char Q = '\"';
private static final String QC = "\",";
private static final String QU = "\"_";
@@ -90,19 +92,35 @@ public final class GelfLayout extends AbstractStringLayout {
return new GelfLayout(host, additionalFields, compressionType, compressionThreshold);
}
+ /**
+ * http://en.wikipedia.org/wiki/Syslog#Severity_levels
+ */
+ static int formatLevel(final Level level) {
+ return Severity.getSeverity(level).getCode();
+ }
+
+ static String formatThrowable(final Throwable throwable) {
+ // stack traces are big enough to provide a reasonably large initial capacity here
+ final StringWriter sw = new StringWriter(2048);
+ final PrintWriter pw = new PrintWriter(sw);
+ throwable.printStackTrace(pw);
+ pw.flush();
+ return sw.toString();
+ }
+
static String formatTimestamp(final long timeMillis) {
return new BigDecimal(timeMillis).divide(TIME_DIVISOR).toPlainString();
}
private final KeyValuePair[] additionalFields;
- private final String host;
-
private final int compressionThreshold;
- private CompressionType compressionType;
+ private final CompressionType compressionType;
- public GelfLayout(final String host, final KeyValuePair[] additionalFields, CompressionType compressionType,
+ private final String host;
+
+ public GelfLayout(final String host, final KeyValuePair[] additionalFields, final CompressionType compressionType,
final int compressionThreshold) {
super(Charsets.UTF_8);
this.host = host;
@@ -135,24 +153,6 @@ public final class GelfLayout extends AbstractStringLayout {
}
}
- private String escapeJson(final String s) {
- return s.replace("\\", "\\\\").replace("\"", "\\\"");
- }
-
- /**
- * http://en.wikipedia.org/wiki/Syslog#Severity_levels
- */
- private int formatLevel(final Level level) {
- return Severity.getSeverity(level).getCode();
- }
-
- private String formatThrowable(final Throwable throwable) {
- final StringWriter sw = new StringWriter();
- final PrintWriter pw = new PrintWriter(sw);
- throwable.printStackTrace(pw);
- return sw.toString();
- }
-
@Override
public Map<String, String> getContentFormat() {
return Collections.emptyMap();
@@ -172,31 +172,34 @@ public final class GelfLayout extends AbstractStringLayout {
@Override
public String toSerializable(final LogEvent event) {
final StringBuilder builder = new StringBuilder(256);
+ JsonStringEncoder jsonEncoder = JsonStringEncoder.getInstance();
builder.append('{');
builder.append("\"version\":\"1.1\",");
- builder.append("\"host\":\"").append(escapeJson(host)).append(QC);
+ builder.append("\"host\":\"").append(jsonEncoder.quoteAsString(host)).append(QC);
builder.append("\"timestamp\":").append(formatTimestamp(event.getTimeMillis())).append(C);
builder.append("\"level\":").append(formatLevel(event.getLevel())).append(C);
if (event.getThreadName() != null) {
- builder.append("\"_thread\":\"").append(escapeJson(event.getThreadName())).append(QC);
+ builder.append("\"_thread\":\"").append(jsonEncoder.quoteAsString(event.getThreadName())).append(QC);
}
if (event.getLoggerName() != null) {
- builder.append("\"_logger\":\"").append(escapeJson(event.getLoggerName())).append(QC);
+ builder.append("\"_logger\":\"").append(jsonEncoder.quoteAsString(event.getLoggerName())).append(QC);
}
for (final KeyValuePair additionalField : additionalFields) {
- builder.append(QU).append(escapeJson(additionalField.getKey())).append("\":\"")
- .append(escapeJson(additionalField.getValue())).append(QC);
+ builder.append(QU).append(jsonEncoder.quoteAsString(additionalField.getKey())).append("\":\"")
+ .append(jsonEncoder.quoteAsString(additionalField.getValue())).append(QC);
}
for (final Map.Entry<String, String> entry : event.getContextMap().entrySet()) {
- builder.append(QU).append(escapeJson(entry.getKey())).append("\":\"").append(escapeJson(entry.getValue()))
- .append(QC);
+ builder.append(QU).append(jsonEncoder.quoteAsString(entry.getKey())).append("\":\"")
+ .append(jsonEncoder.quoteAsString(entry.getValue())).append(QC);
}
if (event.getThrown() != null) {
- builder.append("\"full_message\":\"").append(escapeJson(formatThrowable(event.getThrown()))).append(QC);
+ builder.append("\"full_message\":\"").append(jsonEncoder.quoteAsString(formatThrowable(event.getThrown())))
+ .append(QC);
}
- builder.append("\"short_message\":\"").append(escapeJson(event.getMessage().getFormattedMessage())).append(Q);
+ builder.append("\"short_message\":\"")
+ .append(jsonEncoder.quoteAsString(event.getMessage().getFormattedMessage())).append(Q);
builder.append('}');
return builder.toString();
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/837e0aa1/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest.java
index 207d1b6..0d8dac6 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest.java
@@ -16,10 +16,22 @@
*/
package org.apache.logging.log4j.core.layout;
+import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.List;
+import java.util.zip.GZIPInputStream;
+
+import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.ThreadContext;
-import org.apache.logging.log4j.core.*;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.BasicConfigurationFactory;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.layout.GelfLayout.CompressionType;
import org.apache.logging.log4j.core.util.KeyValuePair;
@@ -28,28 +40,29 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
-import java.util.List;
-
-import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals;
+import com.fasterxml.jackson.core.io.JsonStringEncoder;
public class GelfLayoutTest {
- LoggerContext ctx = (LoggerContext) LogManager.getContext();
- Logger root = ctx.getLogger("");
-
+ static ConfigurationFactory configFactory = new BasicConfigurationFactory();
private static final String HOSTNAME = "TheHost";
+
private static final String KEY1 = "Key1";
private static final String KEY2 = "Key2";
- private static final String VALUE1 = "Value1";
- private static final String VALUE2 = "Value2";
+ private static final String LINE1 = "empty mdc";
+ private static final String LINE2 = "filled mdc";
+ private static final String LINE3 = "error message";
private static final String MDCKEY1 = "MdcKey1";
private static final String MDCKEY2 = "MdcKey2";
private static final String MDCVALUE1 = "MdcValue1";
private static final String MDCVALUE2 = "MdcValue2";
+ private static final String VALUE1 = "Value1";
+ private static final String VALUE2 = "Value2";
- private static final String LINE1 = "empty mdc";
- private static final String LINE2 = "filled mdc";
-
- static ConfigurationFactory configFactory = new BasicConfigurationFactory();
+ @AfterClass
+ public static void cleanupClass() {
+ ConfigurationFactory.removeConfigurationFactory(configFactory);
+ ThreadContext.clearAll();
+ }
@BeforeClass
public static void setupClass() {
@@ -59,30 +72,31 @@ public class GelfLayoutTest {
ctx.reconfigure();
}
- @AfterClass
- public static void cleanupClass() {
- ConfigurationFactory.removeConfigurationFactory(configFactory);
- ThreadContext.clearAll();
- }
+ LoggerContext ctx = (LoggerContext) LogManager.getContext();
+
+ Logger root = ctx.getLogger("");
@Test
public void testLayout() throws Exception {
for (final Appender appender : root.getAppenders().values()) {
root.removeAppender(appender);
}
- // set up appender
+ // set up appenders
final GelfLayout layout = GelfLayout.createLayout(HOSTNAME, new KeyValuePair[] {
new KeyValuePair(KEY1, VALUE1),
new KeyValuePair(KEY2, VALUE2), }, CompressionType.GZIP, 1024);
// ConsoleAppender appender = new ConsoleAppender("Console", layout);
final ListAppender eventAppender = new ListAppender("Events", null, null, true, false);
- final ListAppender appender = new ListAppender("Layouted", null, layout, true, false);
+ final ListAppender rawAppender = new ListAppender("Raw", null, layout, true, true);
+ final ListAppender formattedAppender = new ListAppender("Formatted", null, layout, true, false);
eventAppender.start();
- appender.start();
+ rawAppender.start();
+ formattedAppender.start();
- // set appender on root and set level to debug
+ // set appenders on root and set level to debug
root.addAppender(eventAppender);
- root.addAppender(appender);
+ root.addAppender(rawAppender);
+ root.addAppender(formattedAppender);
root.setLevel(Level.DEBUG);
root.debug(LINE1);
@@ -92,41 +106,67 @@ public class GelfLayoutTest {
root.info(LINE2);
+ final Exception exception = new RuntimeException("some error");
+ root.error(LINE3, exception);
+
ThreadContext.clearMap();
- appender.stop();
+ formattedAppender.stop();
final List<LogEvent> events = eventAppender.getEvents();
- final List<String> list = appender.getMessages();
+ final List<byte[]> raw = rawAppender.getData();
+ final List<String> messages = formattedAppender.getMessages();
//@formatter:off
assertJsonEquals("{" +
- "\"version\": \"1.1\"," +
- "\"host\": \"" + HOSTNAME + "\"," +
- "\"timestamp\": "+GelfLayout.formatTimestamp(events.get(0).getTimeMillis())+"," +
- "\"level\": 7," +
- "\"_thread\": \"main\"," +
- "\"_logger\": \"\"," +
- "\"short_message\": \"" + LINE1 + "\"," +
- "\"_" + KEY1 + "\": \"" + VALUE1 + "\"," +
- "\"_" + KEY2 + "\": \"" + VALUE2 + "\"" +
- "}",
- list.get(0));
+ "\"version\": \"1.1\"," +
+ "\"host\": \"" + HOSTNAME + "\"," +
+ "\"timestamp\": " + GelfLayout.formatTimestamp(events.get(0).getTimeMillis()) + "," +
+ "\"level\": 7," +
+ "\"_thread\": \"main\"," +
+ "\"_logger\": \"\"," +
+ "\"short_message\": \"" + LINE1 + "\"," +
+ "\"_" + KEY1 + "\": \"" + VALUE1 + "\"," +
+ "\"_" + KEY2 + "\": \"" + VALUE2 + "\"" +
+ "}",
+ messages.get(0));
assertJsonEquals("{" +
- "\"version\": \"1.1\"," +
- "\"host\": \"" + HOSTNAME + "\"," +
- "\"timestamp\": "+GelfLayout.formatTimestamp(events.get(1).getTimeMillis())+"," +
- "\"level\": 6," +
- "\"_thread\": \"main\"," +
- "\"_logger\": \"\"," +
- "\"short_message\": \"" + LINE2 + "\"," +
- "\"_" + KEY1 + "\": \"" + VALUE1 + "\"," +
- "\"_" + KEY2 + "\": \"" + VALUE2 + "\"," +
- "\"_" + MDCKEY1 + "\": \"" + MDCVALUE1 + "\"," +
- "\"_" + MDCKEY2 + "\": \"" + MDCVALUE2 + "\"" +
- "}",
- list.get(1));
+ "\"version\": \"1.1\"," +
+ "\"host\": \"" + HOSTNAME + "\"," +
+ "\"timestamp\": " + GelfLayout.formatTimestamp(events.get(1).getTimeMillis()) + "," +
+ "\"level\": 6," +
+ "\"_thread\": \"main\"," +
+ "\"_logger\": \"\"," +
+ "\"short_message\": \"" + LINE2 + "\"," +
+ "\"_" + KEY1 + "\": \"" + VALUE1 + "\"," +
+ "\"_" + KEY2 + "\": \"" + VALUE2 + "\"," +
+ "\"_" + MDCKEY1 + "\": \"" + MDCVALUE1 + "\"," +
+ "\"_" + MDCKEY2 + "\": \"" + MDCVALUE2 + "\"" +
+ "}",
+ messages.get(1));
+
+ final byte[] compressed = raw.get(2);
+ final InputStream gzipStream = new GZIPInputStream(new ByteArrayInputStream(compressed));
+ final byte[] uncompressed = IOUtils.toByteArray(gzipStream);
+ gzipStream.close();
+ final String uncompressedString = new String(uncompressed, layout.getCharset());
+ assertJsonEquals("{" +
+ "\"version\": \"1.1\"," +
+ "\"host\": \"" + HOSTNAME + "\"," +
+ "\"timestamp\": " + GelfLayout.formatTimestamp(events.get(2).getTimeMillis()) + "," +
+ "\"level\": 3," +
+ "\"_thread\": \"main\"," +
+ "\"_logger\": \"\"," +
+ "\"short_message\": \"" + LINE3 + "\"," +
+ "\"full_message\": \"" + String.valueOf(JsonStringEncoder.getInstance().quoteAsString(
+ GelfLayout.formatThrowable(exception))) + "\"," +
+ "\"_" + KEY1 + "\": \"" + VALUE1 + "\"," +
+ "\"_" + KEY2 + "\": \"" + VALUE2 + "\"," +
+ "\"_" + MDCKEY1 + "\": \"" + MDCVALUE1 + "\"," +
+ "\"_" + MDCKEY2 + "\": \"" + MDCVALUE2 + "\"" +
+ "}",
+ uncompressedString);
//@formatter:on
}
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/837e0aa1/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 24781ca..5319e5a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -650,6 +650,12 @@
<version>1.1.6</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.4</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</dependencyManagement>
<build>