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

com.turbospaces.logging.SentryAppender Maven / Gradle / Ivy

There is a newer version: 2.0.33
Show newest version
package com.turbospaces.logging;

import java.util.AbstractMap;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Date;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.StackTraceElementProxy;
import io.sentry.event.Event;
import io.sentry.event.EventBuilder;
import io.sentry.event.interfaces.ExceptionInterface;
import io.sentry.event.interfaces.MessageInterface;
import io.sentry.event.interfaces.SentryException;
import io.sentry.event.interfaces.StackTraceInterface;

public class SentryAppender extends AbstractAppender {
    @Override
    public void start() {
        AlertLoggingFilter filter = (AlertLoggingFilter) context.getObject(Logback.SENTRY_LOGGING_FILTER);
        addFilter(filter);
        filter.start();

        super.start();

        // ~ mark started
        started = true;

        //
        // ~ effectively start
        //
        for (int i = 0; i < threads; i++) {
            WorkerThread worker = workers[i];
            worker.start();
        }
    }
    @Override
    protected boolean dryRun() {
        return alertsDryRun();
    }
    @Override
    protected void sendBulk(List list) {
        if (Objects.nonNull(sentry)) {
            for (SequencedDeferredEvent next : list) {
                sentry.sendEvent(writeBody(next));
            }
        }
    }
    private EventBuilder writeBody(SequencedDeferredEvent data) {
        ILoggingEvent event = data.event();

        EventBuilder builder = new EventBuilder();
        builder.withTimestamp(new Date(event.getTimeStamp()));
        builder.withMessage(event.getFormattedMessage());
        builder.withLogger(event.getLoggerName());
        builder.withLevel(formatLevel(event.getLevel()));
        builder.withExtra(Logback.SEQUENCE, data.seq());

        // ~ write all formatted values
        for (DocumentProperty field : properties.getProperties()) {
            String formatted = field.format(event);
            if (StringUtils.isEmpty(formatted)) {} else {
                builder.withTag(field.getName(), formatted);
            }
        }

        // ~ argument array
        if (event.getArgumentArray() != null) {
            List args = new ArrayList<>();
            for (Object argument : event.getArgumentArray()) {
                args.add((argument != null) ? argument.toString() : null);
            }
            builder.withSentryInterface(new MessageInterface(event.getMessage(), args, event.getFormattedMessage()));
        }

        // ~ exception
        if (event.getThrowableProxy() != null) {
            builder.withSentryInterface(new ExceptionInterface(extractExceptionQueue(event)));
        } else if (event.getCallerData().length > 0) {
            builder.withSentryInterface(new StackTraceInterface(event.getCallerData()));
        }

        // ~ write MDC values
        Map mdc = event.getMDCPropertyMap();
        if (mdc != null) {
            for (Entry entry : mdc.entrySet()) {
                boolean toInclude = true;
                if (mdcNames.isEmpty()) {} else {
                    toInclude = mdcNames.contains(entry.getKey());
                }
                if (toInclude) {
                    if (StringUtils.isEmpty(entry.getValue())) {} else {
                        builder.withTag(entry.getKey(), entry.getValue());
                    }
                }
            }
        }

        return builder;
    }
    public static Deque extractExceptionQueue(ILoggingEvent event) {
        IThrowableProxy throwable = event.getThrowableProxy();
        Deque exceptions = new ArrayDeque<>();
        Set circularityDetector = new HashSet<>();
        StackTraceElement[] enclosingStackTrace = new StackTraceElement[0];

        // Stack the exceptions to send them in the reverse order
        while ( throwable != null ) {
            if (!circularityDetector.add(throwable)) {
                break;
            }

            StackTraceElement[] stackTraceElements = toStackTraceElements(throwable);
            StackTraceInterface stackTrace = new StackTraceInterface(stackTraceElements, enclosingStackTrace);
            Map.Entry mapping = extractPackageAndClassName(throwable.getClassName());
            exceptions.push(new SentryException(throwable.getMessage(), mapping.getKey(), mapping.getValue(), stackTrace));
            enclosingStackTrace = stackTraceElements;
            throwable = throwable.getCause();
        }

        return exceptions;
    }
    public static StackTraceElement[] toStackTraceElements(IThrowableProxy proxy) {
        StackTraceElementProxy[] elementProxies = proxy.getStackTraceElementProxyArray();
        StackTraceElement[] elements = new StackTraceElement[elementProxies.length];

        for (int i = 0; i < elementProxies.length; i++) {
            elements[i] = elementProxies[i].getStackTraceElement();
        }

        return elements;
    }
    public static Map.Entry extractPackageAndClassName(String canonicalClassName) {
        Map.Entry mapping;

        try {
            Class exceptionClass = Class.forName(canonicalClassName);
            Package exceptionPackage = exceptionClass.getPackage();
            String k = exceptionPackage != null ? exceptionPackage.getName() : SentryException.DEFAULT_PACKAGE_NAME;
            String v = exceptionClass.getSimpleName();
            mapping = new AbstractMap.SimpleEntry<>(k, v);
        } catch (ClassNotFoundException e) {
            int lastDot = canonicalClassName.lastIndexOf('.');
            if (lastDot != -1) {
                String k = canonicalClassName.substring(0, lastDot);
                String v = canonicalClassName.substring(lastDot);
                mapping = new AbstractMap.SimpleEntry<>(k, v);
            } else {
                mapping = new AbstractMap.SimpleEntry<>(SentryException.DEFAULT_PACKAGE_NAME, canonicalClassName);
            }
        }

        return mapping;
    }
    public static Event.Level formatLevel(Level level) {
        if (level.isGreaterOrEqual(Level.ERROR)) {
            return Event.Level.ERROR;
        } else if (level.isGreaterOrEqual(Level.WARN)) {
            return Event.Level.WARNING;
        } else if (level.isGreaterOrEqual(Level.INFO)) {
            return Event.Level.INFO;
        } else if (level.isGreaterOrEqual(Level.ALL)) {
            return Event.Level.DEBUG;
        } else {
            return null;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy