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

net.kencochrane.raven.log4j2.SentryAppender Maven / Gradle / Ivy

package net.kencochrane.raven.log4j2;

import com.google.common.base.Splitter;
import net.kencochrane.raven.Raven;
import net.kencochrane.raven.RavenFactory;
import net.kencochrane.raven.dsn.Dsn;
import net.kencochrane.raven.dsn.InvalidDsnException;
import net.kencochrane.raven.event.Event;
import net.kencochrane.raven.event.EventBuilder;
import net.kencochrane.raven.event.interfaces.ExceptionInterface;
import net.kencochrane.raven.event.interfaces.MessageInterface;
import net.kencochrane.raven.event.interfaces.StackTraceInterface;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
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.message.Message;

import java.io.IOException;
import java.util.*;

/**
 * Appender for log4j2 in charge of sending the logged events to a Sentry server.
 */
@Plugin(name = "Raven", category = "Core", elementType = "appender", printObject = true)
public class SentryAppender extends AbstractAppender {
    /**
     * Default name for the appender.
     */
    public static final String APPENDER_NAME = "raven";
    /**
     * Name of the {@link Event#extra} property containing NDC details.
     */
    public static final String LOG4J_NDC = "log4j2-NDC";
    /**
     * Name of the {@link Event#extra} property containing Marker details.
     */
    public static final String LOG4J_MARKER = "log4j2-Marker";
    /**
     * Name of the {@link Event#extra} property containing the Thread name.
     */
    public static final String THREAD_NAME = "Raven-Threadname";
    /**
     * Current instance of {@link Raven}.
     *
     * @see #initRaven()
     */
    protected Raven raven;
    /**
     * DSN property of the appender.
     * 

* Might be null in which case the DSN should be detected automatically. *

*/ protected String dsn; /** * Name of the {@link RavenFactory} being used. *

* Might be null in which case the factory should be defined automatically. *

*/ protected String ravenFactory; /** * Additional tags to be sent to sentry. *

* Might be empty in which case no tags are sent. *

*/ protected Map tags = Collections.emptyMap(); /** * Creates an instance of SentryAppender. */ public SentryAppender() { this(APPENDER_NAME, null); } /** * Creates an instance of SentryAppender. * * @param raven instance of Raven to use with this appender. */ public SentryAppender(Raven raven) { this(APPENDER_NAME, null); this.raven = raven; } private SentryAppender(String name, Filter filter) { super(name, filter, null, true); } /** * Create a Sentry Appender. * * @param name The name of the Appender. * @param dsn Data Source Name to access the Sentry server. * @param ravenFactory Name of the factory to use to build the {@link Raven} instance. * @param tags Tags to add to each event. * @param filter The filter, if any, to use. * @return The SentryAppender. */ @PluginFactory public static SentryAppender createAppender(@PluginAttribute("name") final String name, @PluginAttribute("dsn") final String dsn, @PluginAttribute("ravenFactory") final String ravenFactory, @PluginAttribute("tags") final String tags, @PluginElement("filters") final Filter filter) { if (name == null) { LOGGER.error("No name provided for SentryAppender"); return null; } SentryAppender sentryAppender = new SentryAppender(name, filter); sentryAppender.setDsn(dsn); if (tags != null) sentryAppender.setTags(tags); sentryAppender.setRavenFactory(ravenFactory); return sentryAppender; } /** * Transforms a {@link Level} into an {@link Event.Level}. * * @param level original level as defined in log4j2. * @return log level used within raven. */ protected static Event.Level formatLevel(Level level) { if (level.isAtLeastAsSpecificAs(Level.FATAL)) return Event.Level.FATAL; else if (level.isAtLeastAsSpecificAs(Level.ERROR)) return Event.Level.ERROR; else if (level.isAtLeastAsSpecificAs(Level.WARN)) return Event.Level.WARNING; else if (level.isAtLeastAsSpecificAs(Level.INFO)) return Event.Level.INFO; else return Event.Level.DEBUG; } /** * Extracts message parameters into a List of Strings. *

* null parameters are kept as null. *

* * @param parameters parameters provided to the logging system. * @return the parameters formatted as Strings in a List. */ protected static List formatMessageParameters(Object[] parameters) { List stringParameters = new ArrayList<>(parameters.length); for (Object parameter : parameters) stringParameters.add((parameter != null) ? parameter.toString() : null); return stringParameters; } /** * {@inheritDoc} *

* The raven instance is set in this method instead of {@link #start()} in order to avoid substitute loggers * being generated during the instantiation of {@link Raven}.
*

* * @param logEvent The LogEvent. */ @Override public void append(LogEvent logEvent) { // Do not log the event if the current thread is managed by raven if (Raven.isManagingThread()) return; try { Raven.startManagingThread(); if (raven == null) initRaven(); Event event = buildEvent(logEvent); raven.sendEvent(event); } catch (Exception e) { error("An exception occurred while creating a new event in Raven", logEvent, e); } finally { Raven.stopManagingThread(); } } /** * Initialises the Raven instance. */ protected void initRaven() { try { if (dsn == null) dsn = Dsn.dsnLookup(); raven = RavenFactory.ravenInstance(new Dsn(dsn), ravenFactory); } catch (InvalidDsnException e) { error("An exception occurred during the retrieval of the DSN for Raven", e); } catch (Exception e) { error("An exception occurred during the creation of a Raven instance", e); } } /** * Builds an Event based on the logging event. * * @param event Log generated. * @return Event containing details provided by the logging system. */ protected Event buildEvent(LogEvent event) { Message eventMessage = event.getMessage(); EventBuilder eventBuilder = new EventBuilder() .setTimestamp(new Date(event.getMillis())) .setMessage(eventMessage.getFormattedMessage()) .setLogger(event.getLoggerName()) .setLevel(formatLevel(event.getLevel())) .addExtra(THREAD_NAME, event.getThreadName()); if (!eventMessage.getFormattedMessage().equals(eventMessage.getFormat())) { eventBuilder.addSentryInterface(new MessageInterface(eventMessage.getFormat(), formatMessageParameters(eventMessage.getParameters()))); } if (event.getThrown() != null) { Throwable throwable = event.getThrown(); eventBuilder.addSentryInterface(new ExceptionInterface(throwable)); } else if (event.getSource() != null) { StackTraceElement[] stackTrace = {event.getSource()}; eventBuilder.addSentryInterface(new StackTraceInterface(stackTrace)); } if (event.getSource() != null) { eventBuilder.setCulprit(event.getSource()); } else { eventBuilder.setCulprit(event.getLoggerName()); } if (event.getContextStack() != null) eventBuilder.addExtra(LOG4J_NDC, event.getContextStack().asList()); if (event.getContextMap() != null) { for (Map.Entry mdcEntry : event.getContextMap().entrySet()) { eventBuilder.addExtra(mdcEntry.getKey(), mdcEntry.getValue()); } } if (event.getMarker() != null) eventBuilder.addTag(LOG4J_MARKER, event.getMarker().getName()); for (Map.Entry tagEntry : tags.entrySet()) eventBuilder.addTag(tagEntry.getKey(), tagEntry.getValue()); raven.runBuilderHelpers(eventBuilder); return eventBuilder.build(); } public void setDsn(String dsn) { this.dsn = dsn; } public void setRavenFactory(String ravenFactory) { this.ravenFactory = ravenFactory; } /** * Set the tags that should be sent along with the events. * * @param tags A String of tags. key/values are separated by colon(:) and tags are separated by commas(,). */ public void setTags(String tags) { this.tags = Splitter.on(",").withKeyValueSeparator(":").split(tags); } @Override public void stop() { super.stop(); try { if (raven != null) raven.getConnection().close(); } catch (IOException e) { error("An exception occurred while closing the Raven connection", e); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy