Write log in Json format using log4j

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TimeZone;
import java.util.regex.Pattern;

import org.apache.log4j.Appender;
import org.apache.log4j.Category;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.AppenderAttachable;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.ThrowableInformation;

public class JsonLayout extends Layout {
private static final Pattern SEP_PATTERN = Pattern.compile("(?:\\p{Space}*?[,;]\\p{Space}*)+");
    private static final Pattern PAIR_SEP_PATTERN = Pattern.compile("(?:\\p{Space}*?[:=]\\p{Space}*)+");

    private static final char[] HEX_CHARS =
        {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

    private class LoggerField {
        private String defaultLabel;
        private String renderedLabel;
        private boolean isEnabled = true;

        LoggerField(String defaultName) {
            this.defaultLabel = defaultName;
            this.renderedLabel = defaultName;
        }

        LoggerField(String defaultName, String renderedLabel) {
            this.defaultLabel = defaultName;
            this.renderedLabel = renderedLabel;
        }

        void enable() {
            isEnabled = true;
        }
    }

    private class FieldLabels {
        private final ArrayList<LoggerField> allFields = new ArrayList<LoggerField>();

        final LoggerField exceptionClass = loggerfield("exception.class", "class");
        final LoggerField exceptionMessage = loggerfield("exception.message", "message");
        final LoggerField exceptionStacktrace = loggerfield("exception.stacktrace", "stacktrace");

        final LoggerField exception = loggerfield("exception");
        final LoggerField level = loggerfield("level");
        final LoggerField logger = loggerfield("logger");
        final LoggerField message = loggerfield("message");
        final LoggerField host = loggerfield("host");
        final LoggerField path = loggerfield("path");
        final LoggerField timestamp = loggerfield("timestamp");
        final LoggerField thread = loggerfield("thread");
        final LoggerField version = loggerfield("version");

        private LoggerField loggerfield(String defaultName) {
            return loggerfield(defaultName, null);
        }

        private LoggerField loggerfield(String defaultName, String renderedLabel) {
            LoggerField loggerField;

            if(renderedLabel != null) {
                loggerField = new LoggerField(defaultName, renderedLabel);
            } else {
                loggerField = new LoggerField(defaultName);
            }

            allFields.add(loggerField);

            return loggerField;
        }

        void enable(String fieldName) {
            for (LoggerField field : allFields) {
                if (field.defaultLabel.startsWith(fieldName)) {
                    field.enable();
                }
            }
        }
    }

    private static final String VERSION = "1";

    private String fieldsVal;
    private String includedFields;

    private final Map<String, String> fields;
    private FieldLabels fieldLabels = new FieldLabels();

    private final DateFormat dateFormat;
    private final Date date;
    private final StringBuilder buffer;

    private String path;
    private boolean pathResolved;
    private String hostName;
    private boolean ignoresThrowable;

    public JsonLayout2() {
        fields = new HashMap<String, String>();

        dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));

        date = new Date();
        buffer = new StringBuilder(32*1024);
    }

    @Override
    public String format(LoggingEvent event) {
        buffer.setLength(0);

        buffer.append('{');

        boolean hasPrevField = false;
        if (fieldLabels.exception.isEnabled) {
            hasPrevField = appendException(buffer, event);
        }

        if (hasPrevField) {
            buffer.append(',');
        }
        hasPrevField = appendFields(buffer, event);

        if (fieldLabels.level.isEnabled) {
            if (hasPrevField) {
                buffer.append(',');
            }
            appendField(buffer, fieldLabels.level.renderedLabel, event.getLevel().toString());
            hasPrevField = true;
        }

        if (fieldLabels.logger.isEnabled) {
            if (hasPrevField) {
                buffer.append(',');
            }
            appendField(buffer, fieldLabels.logger.renderedLabel, event.getLoggerName());
            hasPrevField = true;
        }

        if (fieldLabels.message.isEnabled) {
            if (hasPrevField) {
                buffer.append(',');
            }
            appendField(buffer, fieldLabels.message.renderedLabel, event.getRenderedMessage());
            hasPrevField = true;
        }

        if (fieldLabels.host.isEnabled) {
            if (hasPrevField) {
                buffer.append(',');
            }
            appendField(buffer, fieldLabels.host.renderedLabel, hostName);
            hasPrevField = true;
        }

        if (fieldLabels.path.isEnabled) {
            if (hasPrevField) {
                buffer.append(',');
            }
            hasPrevField = appendSourcePath(buffer, event);
        }

        if (fieldLabels.timestamp.isEnabled) {
            if (hasPrevField) {
                buffer.append(',');
            }
            date.setTime(event.getTimeStamp());
            appendField(buffer, fieldLabels.timestamp.renderedLabel, dateFormat.format(date));
            hasPrevField = true;
        }

        if (fieldLabels.thread.isEnabled) {
            if (hasPrevField) {
                buffer.append(',');
            }
            appendField(buffer, fieldLabels.thread.renderedLabel, event.getThreadName());
            hasPrevField = true;
        }

        if (fieldLabels.version.isEnabled) {
            if (hasPrevField) {
                buffer.append(',');
            }
            appendField(buffer, fieldLabels.version.renderedLabel, VERSION);
        }

        buffer.append("}\n");

        return buffer.toString();
    }

    @SuppressWarnings("UnusedParameters")
    private boolean appendFields(StringBuilder buf, LoggingEvent event) {
        if (fields.isEmpty()) {
            return false;
        }

        for (Iterator<Map.Entry<String, String>> iter = fields.entrySet().iterator(); iter.hasNext(); ) {
            Map.Entry<String, String> entry = iter.next();
            appendField(buf, entry.getKey(), entry.getValue());
            if (iter.hasNext()) {
                buf.append(',');
            }
        }

        return true;
    }

    private boolean appendSourcePath(StringBuilder buf, LoggingEvent event) {
        if (!pathResolved) {
            @SuppressWarnings("unchecked")
            Appender appender = findLayoutAppender(event.getLogger());
            if (appender instanceof FileAppender) {
                FileAppender fileAppender = (FileAppender) appender;
                path = getAppenderPath(fileAppender);
            }
            pathResolved = true;
        }
        if (path != null) {
            appendField(buf, fieldLabels.path.renderedLabel, path);
            return true;
        }
        return false;
    }

    private Appender findLayoutAppender(Category logger) {
        for(Category parent = logger; parent != null; parent = parent.getParent()) {
            @SuppressWarnings("unchecked")
            Appender appender = findLayoutAppender(parent.getAllAppenders());
            if(appender != null) {
                return appender;
            }
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    private Appender findLayoutAppender(Enumeration<? extends Appender> appenders) {
        if(appenders == null) {
            return null;
        }

        while (appenders.hasMoreElements()) {
            Appender appender = appenders.nextElement();
            // get the first appender with this layout instance and ignore others;
            // actually a single instance of this class is not intended to be used with multiple threads.
            if (appender.getLayout() == this) {
                return appender;
            }
            if (appender instanceof AppenderAttachable) {
                AppenderAttachable appenderContainer = (AppenderAttachable) appender;
                return findLayoutAppender(appenderContainer.getAllAppenders());
            }
        }
        return null;
    }

    private String getAppenderPath(FileAppender fileAppender) {
        String path = null;
        try {
            String fileName = fileAppender.getFile();
            if (fileName != null && !fileName.isEmpty()) {
                path = new File(fileName).getCanonicalPath();
            }
        } catch (IOException e) {
            LogLog.error("Unable to retrieve appender's file name", e);
        }
        return path;
    }

    private boolean appendException(StringBuilder buffer, LoggingEvent event) {
        ThrowableInformation throwableInfo = event.getThrowableInformation();
        if (throwableInfo == null) {
            return false;
        }

        appendQuotedName(buffer, fieldLabels.exception.renderedLabel);
        buffer.append(":{");

        boolean hasPrevField = false;

        @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
        Throwable throwable = throwableInfo.getThrowable();
        if (throwable != null) {
            String message = throwable.getMessage();
            if (message != null) {
                appendField(buffer, fieldLabels.exceptionMessage.renderedLabel, message);
                hasPrevField = true;
            }

            String className = throwable.getClass().getCanonicalName();
            if (className != null) {
                if (hasPrevField) {
                    buffer.append(',');
                }
                appendField(buffer, fieldLabels.exceptionClass.renderedLabel, className);
                hasPrevField = true;
            }
        }

        String[] stackTrace = throwableInfo.getThrowableStrRep();
        if (stackTrace != null && stackTrace.length != 0) {
            if (hasPrevField) {
                buffer.append(',');
            }
            appendQuotedName(buffer, fieldLabels.exceptionStacktrace.renderedLabel);
            buffer.append(":\"");
            for (int i = 0, len = stackTrace.length; i < len; i++) {
                appendValue(buffer, stackTrace[i]);
                if (i != len - 1) {
                    appendChar(buffer, '\n');
                }
            }
            buffer.append('\"');
        }

        buffer.append('}');

        return true;
    }

    @Override
    public boolean ignoresThrowable() {
        return ignoresThrowable;
    }

    public void activateOptions() {
        fieldLabels = new FieldLabels();

        if (includedFields != null) {
            String[] included = SEP_PATTERN.split(includedFields);
            for (String val : included) {
                fieldLabels.enable(val);
            }
        }
        if (fieldsVal != null) {
            String[] fields = SEP_PATTERN.split(fieldsVal);
            for (String fieldVal : fields) {
                String[] field = PAIR_SEP_PATTERN.split(fieldVal);
                this.fields.put(field[0], field[1]);
            }
        }
        if (hostName == null) {
            try {
                hostName = InetAddress.getLocalHost().getHostName();
            } catch (UnknownHostException e) {
                hostName = "localhost";
                LogLog.error("Unable to determine name of the localhost", e);
            }
        }
        ignoresThrowable = !fieldLabels.exception.isEnabled;
    }

    @Override
    public String getContentType() {
        return "application/json";
    }

    private void appendQuotedName(StringBuilder out, Object name) {
        out.append('\"');
        appendValue(out, String.valueOf(name));
        out.append('\"');
    }

    private void appendQuotedValue(StringBuilder out, Object val) {
        out.append('\"');
        appendValue(out, String.valueOf(val));
        out.append('\"');
    }

    private void appendValue(StringBuilder out, String val) {
        for (int i = 0, len = val.length(); i < len; i++) {
            appendChar(out, val.charAt(i));
        }
    }

    private void appendField(StringBuilder out, Object name, Object val) {
        appendQuotedName(out, name);
        out.append(':');
        appendQuotedValue(out, val);
    }

    private void appendChar(StringBuilder out, char ch) {
        switch (ch) {
            case '"':
                out.append("\\\"");
                break;
            case '\\':
                out.append("\\\\");
                break;
            case '/':
                out.append("\\/");
                break;
            case '\b':
                out.append("\\b");
                break;
            case '\f':
                out.append("\\f");
                break;
            case '\n':
                out.append("\\n");
                break;
            case '\r':
                out.append("\\r");
                break;
            case '\t':
                out.append("\\t");
                break;
            default:
                if ((ch <= '\u001F') || ('\u007F' <= ch && ch <= '\u009F') || ('\u2000' <= ch && ch <= '\u20FF')) {
                    out.append("\\u")
                        .append(HEX_CHARS[ch >> 12 & 0x000F])
                        .append(HEX_CHARS[ch >> 8 & 0x000F])
                        .append(HEX_CHARS[ch >> 4 & 0x000F])
                        .append(HEX_CHARS[ch & 0x000F]);
                } else {
                    out.append(ch);
                }
                break;
        }
    }

    public void setFields(String fields) {
        this.fieldsVal = fields;
    }

    public void setIncludedFields(String includedFields) {
        this.includedFields = includedFields;
    }

    public void setHostName(String hostName) {
        this.hostName = hostName;
    }
}


Here is the configuration to add in log4j.properties file
log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender
log4j.logger.com.demo.logger=debug, A1
log4j.appender.A1.File=${catalina.base}/jsonlog.log
log4j.appender.A1.Append=true
log4j.appender.A1.layout=com.demo.logger.JsonLayout

Comments

Popular posts from this blog

Install Alfresco Content Service 6.0 on ubuntu 16 using distribution zip

Lucene and fts-search

Call javascript webscript from contoller