All Downloads are FREE. Search and download functionalities are using the official Maven repository.

me.moocar.logbackgelf.GelfAppender Maven / Gradle / Ivy

package me.moocar.logbackgelf;

import ch.qos.logback.core.AppenderBase;

import java.io.*;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Map;

/**
 * Responsible for Formatting a log event and sending it to a Graylog2 Server. Note that you can't swap in a different
 * Layout since the GELF format is static.
 */
public class GelfAppender extends AppenderBase {

    // The following are configurable via logback configuration
    private String facility = "GELF";
    private String graylog2ServerHost = "localhost";
    private int graylog2ServerPort = 12201;
    private boolean useLoggerName = false;
    private boolean useThreadName = false;
    private String graylog2ServerVersion = "0.9.5";
    private int chunkThreshold = 1000;
    private String messagePattern = "%m%rEx";
    private Map additionalFields = new HashMap();

    // The following are hidden (not configurable)
    private int shortMessageLength = 255;
    private static final int maxChunks = 127;
    private int messageIdLength = 32;
    private boolean padSeq = true;
    private final byte[] chunkedGelfId = new byte[]{0x1e, 0x0f};

    private AppenderExecutor appenderExecutor;

    /**
     * The main append method. Takes the event that is being logged, formats if for GELF and then sends it over the wire
     * to the log server
     *
     * @param logEvent The event that we are logging
     */
    @Override
    protected void append(E logEvent) {

        try {

            appenderExecutor.append(logEvent);

        } catch (RuntimeException e) {
            System.out.println(getStringStackTrace(e));
            this.addError("Error occurred: ", e);
            throw e;
        }
    }

    private String getStringStackTrace(Exception e) {
        Writer result = new StringWriter();
        PrintWriter printWriter = new PrintWriter(result);
        e.printStackTrace(printWriter);
        return result.toString();
    }

    @Override
    public void start() {
        super.start();
        initExecutor();
    }

    /**
     * This is an ad-hoc dependency injection mechanism. We don't want create all these classes every time a message is
     * logged. They will hang around for the lifetime of the appender.
     */
    private void initExecutor() {

        try {

            InetAddress address = getInetAddress();

            Transport transport = new Transport(graylog2ServerPort, address);

            if (graylog2ServerVersion.equals("0.9.6")) {
                messageIdLength = 8;
                padSeq = false;
            }

            String hostname = InetAddress.getLocalHost().getHostName();

            PayloadChunker payloadChunker = new PayloadChunker(chunkThreshold, maxChunks,
                    new MessageIdProvider(messageIdLength, MessageDigest.getInstance("MD5"), hostname),
                    new ChunkFactory(chunkedGelfId, padSeq));

            GelfConverter converter = new GelfConverter(facility, useLoggerName, useThreadName, additionalFields, shortMessageLength, hostname, messagePattern);

            appenderExecutor = new AppenderExecutor(transport, payloadChunker, converter, new Zipper(), chunkThreshold);

        } catch (Exception e) {

            throw new RuntimeException("Error initialising appender appenderExecutor", e);
        }
    }

    /**
     * Gets the Inet address for the graylog2ServerHost and gives a specialised error message if an exception is thrown
     *
     * @return The Inet address for graylog2ServerHost
     */
    private InetAddress getInetAddress() {
        try {
            return InetAddress.getByName(graylog2ServerHost);
        } catch (UnknownHostException e) {
            throw new IllegalStateException("Unknown host: " + e.getMessage() +
                    ". Make sure you have specified the 'graylog2ServerHost' property correctly in your logback.xml'");
        }
    }

    //////////// Logback Property Getter/Setters ////////////////

    /**
     * The name of your service. Appears in facility column in graylog2-web-interface
     */
    public String getFacility() {
        return facility;
    }

    public void setFacility(String facility) {
        this.facility = facility;
    }

    /**
     * The hostname of the graylog2 server to send messages to
     */
    public String getGraylog2ServerHost() {
        return graylog2ServerHost;
    }

    public void setGraylog2ServerHost(String graylog2ServerHost) {
        this.graylog2ServerHost = graylog2ServerHost;
    }

    /**
     * The port of the graylog2 server to send messages to
     */
    public int getGraylog2ServerPort() {
        return graylog2ServerPort;
    }

    public void setGraylog2ServerPort(int graylog2ServerPort) {
        this.graylog2ServerPort = graylog2ServerPort;
    }

    /**
     * If true, an additional field call "_loggerName" will be added to each gelf message. Its contents will be the
     * fully qualified name of the logger. e.g: com.company.Thingo.
     */
    public boolean isUseLoggerName() {
        return useLoggerName;
    }

    public void setUseLoggerName(boolean useLoggerName) {
        this.useLoggerName = useLoggerName;
    }
    
    /**
     * If true, an additional field call "_threadName" will be added to each gelf message. Its contents will be the
     * Name of the thread. Defaults to "false".
     */
    public boolean isUseThreadName() {
    	return useThreadName;
    }
    
    public void setUseThreadName(boolean useThreadName) {
    	this.useThreadName = useThreadName;
    }

    /**
     * additional fields to add to the gelf message. Here's how these work: 
Let's take an example. I want to log * the client's ip address of every request that comes into my web server. To do this, I add the ipaddress to the * slf4j MDC on each request as follows: ... MDC.put("ipAddress", "44.556.345.657"); ... Now, to * include the ip address in the gelf message, i just add the following to my logback.groovy: * appender("GELF", GelfAppender) { ... additionalFields = [identity:"_identity"] ... } in the * additionalFields map, the key is the name of the MDC to look up. the value is the name that should be given to * the key in the additional field in the gelf message. */ public Map getAdditionalFields() { return additionalFields; } public void setAdditionalFields(Map additionalFields) { this.additionalFields = additionalFields; } /** * Add an additional field. This is mainly here for compatibility with logback.xml * * @param keyValue This must be in format key:value where key is the MDC key, and value is the GELF field * name. e.g "ipAddress:_ip_address" */ public void addAdditionalField(String keyValue) { String[] splitted = keyValue.split(":"); if (splitted.length != 2) { throw new IllegalArgumentException("additionalField must be of the format key:value, where key is the MDC " + "key, and value is the GELF field name. But found '" + keyValue + "' instead."); } additionalFields.put(splitted[0], splitted[1]); } /** * The length of the message to truncate to */ public int getShortMessageLength() { return shortMessageLength; } public void setShortMessageLength(int shortMessageLength) { this.shortMessageLength = shortMessageLength; } public String getGraylog2ServerVersion() { return graylog2ServerVersion; } public void setGraylog2ServerVersion(String graylog2ServerVersion) { this.graylog2ServerVersion = graylog2ServerVersion; } public int getChunkThreshold() { return chunkThreshold; } public void setChunkThreshold(int chunkThreshold) { this.chunkThreshold = chunkThreshold; } public String getMessagePattern() { return messagePattern; } public void setMessagePattern(String messagePattern) { this.messagePattern = messagePattern; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy