@@ -0,0 +1,3150 @@
+package net.kencochrane.raven.log4j2;%0A%0Aimport net.kencochrane.raven.Raven;%0Aimport net.kencochrane.raven.event.Event;%0Aimport net.kencochrane.raven.event.EventBuilder;%0Aimport net.kencochrane.raven.event.interfaces.ExceptionInterface;%0Aimport net.kencochrane.raven.event.interfaces.StackTraceInterface;%0Aimport org.apache.logging.log4j.Level;%0Aimport org.apache.logging.log4j.core.LogEvent;%0Aimport org.apache.logging.log4j.core.appender.AbstractAppender;%0A%0Aimport java.io.Serializable;%0Aimport java.util.Date;%0Aimport java.util.Map;%0A%0Apublic class SentryAppender extends AbstractAppender%3CSerializable%3E %7B%0A    public static final String APPENDER_NAME = %22raven%22;%0A    private static final String LOG4J_NDC = %22Log4J-NDC%22;%0A    private final Raven raven;%0A%0A    public SentryAppender() %7B%0A        this(new Raven());%0A    %7D%0A%0A    public SentryAppender(Raven raven) %7B%0A        super(APPENDER_NAME, null, null);%0A        this.raven = raven;%0A    %7D%0A%0A    private static Event.Level formatLevel(Level level) %7B%0A        switch (level) %7B%0A            case FATAL:%0A                return Event.Level.FATAL;%0A            case ERROR:%0A                return Event.Level.ERROR;%0A            case WARN:%0A                return Event.Level.WARNING;%0A            case INFO:%0A                return Event.Level.INFO;%0A            case DEBUG:%0A            case TRACE:%0A                return Event.Level.DEBUG;%0A            default:%0A                return null;%0A        %7D%0A    %7D%0A%0A    @Override%0A    public void append(LogEvent event) %7B%0A        EventBuilder eventBuilder = new EventBuilder()%0A                .setTimestamp(new Date(event.getMillis()))%0A                .setMessage(event.getMessage().getFormattedMessage())%0A                .setLogger(event.getLoggerName())%0A                .setLevel(formatLevel(event.getLevel()))%0A                .setCulprit(formatCulprit(event.getSource()));%0A%0A        if (event.getThrown() != null) %7B%0A            Throwable throwable = event.getThrown();%0A            eventBuilder.addSentryInterface(new ExceptionInterface(throwable))%0A                    .addSentryInterface(new StackTraceInterface(throwable));%0A            eventBuilder.setCulprit(throwable);%0A        %7D else %7B%0A            // When it's a message try to rely on the position of the log (the same message can be logged from%0A            // different places, or a same place can log a message in different ways.%0A            eventBuilder.generateChecksum(formatCulprit(event.getSource()));%0A        %7D%0A%0A        if (event.getContextStack() != null) %7B%0A            eventBuilder.addExtra(LOG4J_NDC, event.getContextStack().asList());%0A        %7D%0A%0A        if (event.getContextMap() != null) %7B%0A            for (Map.Entry%3CString, String%3E mdcEntry : event.getContextMap().entrySet()) %7B%0A                eventBuilder.addExtra(mdcEntry.getKey(), mdcEntry.getValue());%0A            %7D%0A        %7D%0A%0A        raven.runBuilderHelpers(eventBuilder);%0A%0A        raven.sendEvent(eventBuilder.build());%0A    %7D%0A%0A    private String formatCulprit(StackTraceElement stackTraceElement) %7B%0A        return stackTraceElement.getClassName() + %22.%22 + stackTraceElement.getMethodName()%0A                + %22 at line %22 + stackTraceElement.getLineNumber();%0A    %7D%0A%7D%0A

Create a basic Appender for log4j2