--- GelfLayout.java.orig 2016-12-07 11:19:27.548000000 +0200 +++ GelfLayout.java 2016-12-07 11:39:10.984000000 +0200 @@ -1,4 +1,4 @@ -/* +/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -21,6 +21,8 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Map; @@ -28,13 +30,17 @@ import java.util.zip.GZIPOutputStream; import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.Node; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.config.plugins.PluginAttribute; import org.apache.logging.log4j.core.config.plugins.PluginElement; import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.apache.logging.log4j.core.lookup.StrSubstitutor; import org.apache.logging.log4j.core.net.Severity; import org.apache.logging.log4j.core.util.JsonUtils; import org.apache.logging.log4j.core.util.KeyValuePair; @@ -106,15 +112,17 @@ private final CompressionType compressionType; private final String host; private final boolean includeStacktrace; + private final boolean includeThreadContext; public GelfLayout(final String host, final KeyValuePair[] additionalFields, final CompressionType compressionType, - final int compressionThreshold, final boolean includeStacktrace) { + final int compressionThreshold, final boolean includeStacktrace, final boolean includeThreadContext) { super(StandardCharsets.UTF_8); - this.host = host; + this.host = getHostName(host); this.additionalFields = additionalFields; this.compressionType = compressionType; this.compressionThreshold = compressionThreshold; this.includeStacktrace = includeStacktrace; + this.includeThreadContext = includeThreadContext; } @PluginFactory @@ -127,9 +135,10 @@ @PluginAttribute(value = "compressionThreshold", defaultInt = COMPRESSION_THRESHOLD) final int compressionThreshold, @PluginAttribute(value = "includeStacktrace", - defaultBoolean = true) final boolean includeStacktrace) { + defaultBoolean = true) final boolean includeStacktrace, + @PluginAttribute(value = "includeThreadContext", defaultBoolean = true) boolean includeThreadContext) { // @formatter:on - return new GelfLayout(host, additionalFields, compressionType, compressionThreshold, includeStacktrace); + return new GelfLayout(host, additionalFields, compressionType, compressionThreshold, includeStacktrace, includeThreadContext); } @Override @@ -184,9 +193,7 @@ } private StringBuilder toText(final LogEvent event, final StringBuilder builder, final boolean gcFree) { - builder.append('{'); - builder.append("\"version\":\"1.1\","); - builder.append("\"host\":\""); + builder.append("{\"version\":\"1.1\",\"host\":\""); JsonUtils.quoteAsString(toNullSafeString(host), builder); builder.append(QC); builder.append("\"timestamp\":").append(formatTimestamp(event.getTimeMillis())).append(C); @@ -202,14 +209,12 @@ builder.append(QC); } - for (final KeyValuePair additionalField : additionalFields) { - builder.append(QU); - JsonUtils.quoteAsString(additionalField.getKey(), builder); - builder.append("\":\""); - JsonUtils.quoteAsString(toNullSafeString(additionalField.getValue()), builder); - builder.append(QC); + if (additionalFields.length > 0) { + additionalFieldsToText(event, builder); + } + if (includeThreadContext) { + event.getContextData().forEach(WRITE_KEY_VALUES_INTO, builder); } - event.getContextData().forEach(WRITE_KEY_VALUES_INTO, builder); if (event.getThrown() != null) { builder.append("\"full_message\":\""); if (includeStacktrace) { @@ -240,6 +245,23 @@ return builder; } + private void additionalFieldsToText(final LogEvent event, final StringBuilder builder) { + // somehow the local configuration member on the layout is always null + Configuration config = configuration; + if (config == null) { + config = ((LoggerContext) LogManager.getContext(false)).getConfiguration(); + } + final StrSubstitutor strSubstitutor = config.getStrSubstitutor(); + for (final KeyValuePair additionalField : additionalFields) { + builder.append(QU); + JsonUtils.quoteAsString(additionalField.getKey(), builder); + builder.append("\":\""); + final String value = strSubstitutor.replace(event, additionalField.getValue()); + JsonUtils.quoteAsString(toNullSafeString(value), builder); + builder.append(QC); + } + } + private static final TriConsumer WRITE_KEY_VALUES_INTO = new TriConsumer() { @Override public void accept(final String key, final Object value, final StringBuilder stringBuilder) { @@ -295,7 +317,7 @@ /** * http://en.wikipedia.org/wiki/Syslog#Severity_levels */ - private int formatLevel(final Level level) { + private static int formatLevel(final Level level) { return Severity.getSeverity(level).getCode(); } @@ -310,4 +332,24 @@ pw.flush(); return sw.getBuffer(); } -} + + /** + * Resolves the HOST attribute in case it is not provided to the local host name. + * + * @param host + * @return + */ + private static String getHostName(final String host) { + if (host != null) { + return host; + } + + try { + return InetAddress.getLocalHost().getHostName(); + } + catch (final UnknownHostException e) { + StatusLogger.getLogger().warn("Could not detect localhost name, falling back to \"localhost\""); + return "localhost"; + } + } +} \ No newline at end of file