package org.graylog2;

import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.MDC;
import org.apache.log4j.spi.LocationInfo;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.ThrowableInformation;
import org.graylog2.log.Log4jVersionChecker;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Map;

public class GelfMessageFactory {

    private static final int MAX_SHORT_MESSAGE_LENGTH = 250;
    private static final String ORIGIN_HOST_KEY = "originHost";
    private static final String LOGGER_NAME = "logger";
    private static final String LOGGER_NDC = "loggerNdc";
    private static final String THREAD_NAME = "thread";
    private static final String JAVA_TIMESTAMP = "timestampMs";

    @SuppressWarnings("unchecked")
    public static GelfMessage makeMessage(Layout layout, LoggingEvent event, GelfMessageProvider provider) {
        long timeStamp = Log4jVersionChecker.getTimeStamp(event);
        Level level = event.getLevel();

        String file = null;
        String lineNumber = null;
        if (provider.isIncludeLocation()) {
            LocationInfo locationInformation = event.getLocationInformation();
            file = locationInformation.getFileName();
            lineNumber = locationInformation.getLineNumber();
        }

        String renderedMessage = layout != null ? layout.format(event) : event.getRenderedMessage();
        String shortMessage;

        if (renderedMessage == null) {
            renderedMessage = "";
        }

        if (provider.isExtractStacktrace()) {
            ThrowableInformation throwableInformation = event.getThrowableInformation();
            if (throwableInformation != null) {
                renderedMessage += "\n\r" + extractStacktrace(throwableInformation);
            }
        }

        if (renderedMessage.length() > MAX_SHORT_MESSAGE_LENGTH) {
            shortMessage = renderedMessage.substring(0, MAX_SHORT_MESSAGE_LENGTH - 1);
        } else {
            shortMessage = renderedMessage;
        }

        GelfMessage gelfMessage = new GelfMessage(shortMessage, renderedMessage, timeStamp,
                String.valueOf(level.getSyslogEquivalent()), lineNumber, file);

        if (provider.getOriginHost() != null) {
            gelfMessage.setHost(provider.getOriginHost());
        }

        if (provider.getFacility() != null) {
            gelfMessage.setFacility(provider.getFacility());
        }

        Map<String, String> fields = provider.getFields();
        for (Map.Entry<String, String> entry : fields.entrySet()) {
            if (entry.getKey().equals(ORIGIN_HOST_KEY) && gelfMessage.getHost() == null) {
                gelfMessage.setHost(fields.get(ORIGIN_HOST_KEY));
            } else {
                gelfMessage.addField(entry.getKey(), entry.getValue());
            }
        }

        if (provider.isAddExtendedInformation()) {

            gelfMessage.addField(THREAD_NAME, event.getThreadName());
            gelfMessage.addField(LOGGER_NAME, event.getLoggerName());
            gelfMessage.addField(JAVA_TIMESTAMP, Long.toString(gelfMessage.getJavaTimestamp()));

            // Get MDC and add a GELF field for each key/value pair
            Map<String, Object> mdc = MDC.getContext();

            if (mdc != null) {
                for (Map.Entry<String, Object> entry : mdc.entrySet()) {
                    Object value = provider.transformExtendedField(entry.getKey(), entry.getValue());
                    gelfMessage.addField(entry.getKey(), value);
                }
            }

            // Get NDC and add a GELF field
            String ndc = event.getNDC();

            if (ndc != null) {
                gelfMessage.addField(LOGGER_NDC, ndc);
            }
        }

        return gelfMessage;
    }

    private static String extractStacktrace(ThrowableInformation throwableInformation) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        throwableInformation.getThrowable().printStackTrace(pw);
        return sw.toString();
    }
}

@@ -1716,32 +1716,155 @@
 tion != null) %7B%0A
+                String stackTrace = extractStacktrace(throwableInformation);%0A                if (stackTrace != null) %7B%0A    
                 
@@ -1924,32 +1924,50 @@
 leInformation);%0A
+                %7D%0A
             %7D%0A  
@@ -4151,16 +4151,30 @@
 %0A       
+ Throwable t =
  throwab
@@ -4201,16 +4201,56 @@
 owable()
+;%0A        if (t != null) %7B%0A            t
 .printSt
@@ -4267,24 +4267,28 @@
 w);%0A        
+ 
+   
 return sw.to
@@ -4293,20 +4293,72 @@
 toString();%0A
+        %7D else %7B%0A            return null;%0A        %7D%0A
     %7D%0A%7D%0A

Fix NPE with null throwable