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

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

The newest version!
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.environment.RavenEnvironment;
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.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(); /** * Set of tags to look for in the MDC. These will be added as tags to be sent to sentry. *

* Might be empty in which case no mapped tags are set. *

*/ private Set extraTags = Collections.emptySet(); /** * 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 extraTags Tags to search through the MDC. * @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, @PluginAttribute("extraTags") final String extraTags, @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); if (extraTags != null) sentryAppender.setExtraTags(extraTags); 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.isMoreSpecificThan(Level.FATAL)) return Event.Level.FATAL; else if (level.isMoreSpecificThan(Level.ERROR)) return Event.Level.ERROR; else if (level.isMoreSpecificThan(Level.WARN)) return Event.Level.WARNING; else if (level.isMoreSpecificThan(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 (RavenEnvironment.isManagingThread()) return; RavenEnvironment.startManagingThread(); try { 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 { RavenEnvironment.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() .withTimestamp(new Date(event.getTimeMillis())) .withMessage(eventMessage.getFormattedMessage()) .withLogger(event.getLoggerName()) .withLevel(formatLevel(event.getLevel())) .withExtra(THREAD_NAME, event.getThreadName()); if (!eventMessage.getFormattedMessage().equals(eventMessage.getFormat())) { eventBuilder.withSentryInterface(new MessageInterface(eventMessage.getFormat(), formatMessageParameters(eventMessage.getParameters()))); } Throwable throwable = event.getThrown(); if (throwable != null) { eventBuilder.withSentryInterface(new ExceptionInterface(throwable)); } else if (event.getSource() != null) { StackTraceElement[] stackTrace = {event.getSource()}; eventBuilder.withSentryInterface(new StackTraceInterface(stackTrace)); } if (event.getSource() != null) { eventBuilder.withCulprit(event.getSource()); } else { eventBuilder.withCulprit(event.getLoggerName()); } if (event.getContextStack() != null) eventBuilder.withExtra(LOG4J_NDC, event.getContextStack().asList()); if (event.getContextMap() != null) { for (Map.Entry mdcEntry : event.getContextMap().entrySet()) { if (extraTags.contains(mdcEntry.getKey())) { eventBuilder.withTag(mdcEntry.getKey(), mdcEntry.getValue()); } else { eventBuilder.withExtra(mdcEntry.getKey(), mdcEntry.getValue()); } } } if (event.getMarker() != null) eventBuilder.withTag(LOG4J_MARKER, event.getMarker().getName()); for (Map.Entry tagEntry : tags.entrySet()) eventBuilder.withTag(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); } /** * Set the mapped extras that will be used to search MDC and upgrade key pair to a tag sent along with the events. * * @param extraTags A String of mappedTags. mappedTags are separated by commas(,). */ public void setExtraTags(String extraTags) { this.extraTags = new HashSet<>(Arrays.asList(extraTags.split(","))); } @Override public void stop() { RavenEnvironment.startManagingThread(); try { if (!isStarted()) return; super.stop(); if (raven != null) raven.closeConnection(); } catch (Exception e) { error("An exception occurred while closing the Raven connection", e); } finally { RavenEnvironment.stopManagingThread(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy