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

backtrace.io.log4j2.Appender Maven / Gradle / Ivy

package backtrace.io.log4j2;


import backtrace.io.BacktraceClient;
import backtrace.io.BacktraceConfig;
import backtrace.io.data.BacktraceReport;
import org.apache.logging.log4j.core.Core;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.Property;
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.status.StatusLogger;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Plugin(
        name = "BacktraceAppender",
        category = Core.CATEGORY_NAME,
        elementType = org.apache.logging.log4j.core.Appender.ELEMENT_TYPE)

public class Appender extends AbstractAppender {
    private final BacktraceClient backtraceClient;

    private final StatusLogger internalLogger = StatusLogger.getLogger();
    private final static String ATTRIBUTE_LOGGING_LEVEL_NAME = "log_level";

    protected Appender(BacktraceClient backtraceClient, String name, Filter filter, Layout layout, boolean ignoreExceptions, Property[] properties) {
        super(name, filter, layout, ignoreExceptions, properties);
        this.backtraceClient = backtraceClient;
        this.addFilter(new BacktraceLogsFilter());
    }

    @PluginFactory
    @SuppressWarnings("unused")
    public static Appender createAppender(
            @PluginAttribute("name") String name,
            @PluginElement("Layout") Layout layout,
            @PluginElement("Filter") final Filter filter,
            @PluginAttribute(value = "endpointUrl") String endpointUrl,
            @PluginAttribute(value = "submissionToken") String submissionToken,
            @PluginAttribute(value = "submissionUrl") String submissionUrl,
            @PluginAttribute(value = "enableUncaughtExceptionHandler") Boolean enableUncaughtExceptionHandler,
            @PluginAttribute(value = "appVersion") String appVersion,
            @PluginAttribute(value = "appName") String appName,
            @PluginAttribute(value = "useDatabase") boolean useDatabase,
            @PluginAttribute(value = "maxDatabaseSize", defaultLong = -1) long maxDatabaseSize, // -1 is unlimited
            @PluginAttribute(value = "maxDatabaseRecordCount", defaultInt = -1) int maxDatabaseRecordCount, // -1 is unlimited
            @PluginAttribute(value = "maxDatabaseRetryLimit", defaultInt = 3) int maxDatabaseRetryLimit
    ) {
        if (name == null) {
            LOGGER.error("No name provided for BacktraceAppender");
            return null;
        }

        BacktraceConfig backtraceConfig = Appender.createBacktraceConfig(submissionUrl, endpointUrl, submissionToken,
                useDatabase, maxDatabaseSize,
                maxDatabaseRecordCount, maxDatabaseRetryLimit);

        BacktraceClient backtraceClient = Appender.createBacktraceClient(backtraceConfig, enableUncaughtExceptionHandler, appName, appVersion);

        return new Appender(backtraceClient, name, filter, layout, true, null);
    }

    BacktraceClient getBacktraceClient() {
        return backtraceClient;
    }

    /**
     * Check is passed string is not null and not empty otherwise true
     *
     * @param s string
     * @return false if string is null or empty
     */
    private static boolean isStringNotEmpty(final String s) {
        // Null-safe, short-circuit evaluation.
        return !(s == null || s.trim().isEmpty());
    }

    /**
     * Send log message to Backtrace console
     */
    @Override
    public void append(LogEvent logEvent) {
        if (logEvent == null) {
            return;
        }

        internalLogger.debug("Sending report with message " + logEvent.getMessage().getFormattedMessage());
        BacktraceReport report = createBacktraceReport(logEvent);
        this.getBacktraceClient().send(report);
    }


    /**
     * Close all of Backtrace library resources and wait until last of messages will be process and
     */
    @Override
    public void stop() {
        internalLogger.debug("Closing BacktraceAppender");
        try {
            super.stop();
            this.getBacktraceClient().close();
        } catch (InterruptedException e) {
            internalLogger.error("Error occurs during closing Backtrace client", e);
        }
    }


    /**
     * Wait until last of messages will be process and close all of Backtrace library resources
     *
     * @throws InterruptedException if the current thread is interrupted while waiting
     */
    public void await() throws InterruptedException {
        this.getBacktraceClient().await();
    }

    /**
     * Wait until all messages in queue will be sent
     *
     * @param timeout the maximum time to wait
     * @param unit    the time unit of the {@code timeout} argument
     * @return {@code true} if all messages are sent in passed time and {@code false}
     * if the waiting time elapsed before all messages has been sent
     * @throws InterruptedException if the current thread is interrupted while waiting
     */
    public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
        return this.getBacktraceClient().await(timeout, unit);
    }

    /**
     * Create instance of Backtrace Client based on properties from configuration file (eg. log4j.properties)
     *
     * @return Backtrace library configuration
     */
    static BacktraceConfig createBacktraceConfig(String submissionUrl, String endpointUrl, String submissionToken,
                                                 boolean useDatabase, Long maxDatabaseSize,
                                                 Integer maxDatabaseRecordCount, Integer maxDatabaseRetryLimit) {
        BacktraceConfig backtraceConfig = isStringNotEmpty(submissionUrl) ? new BacktraceConfig(submissionUrl) : new BacktraceConfig(endpointUrl, submissionToken);

        if (!useDatabase) {
            backtraceConfig.disableDatabase();
            return backtraceConfig;
        }

        if (maxDatabaseSize != null) {
            backtraceConfig.setMaxDatabaseSize(maxDatabaseSize);
        }

        if (maxDatabaseRecordCount != null) {
            backtraceConfig.setMaxRecordCount(maxDatabaseRecordCount);
        }

        if (maxDatabaseRetryLimit != null) {
            backtraceConfig.setDatabaseRetryLimit(maxDatabaseRetryLimit);
        }

        return backtraceConfig;
    }

    /**
     * Configure Backtrace Client - enable uncaught exception handler, set app name and app version
     *
     * @param config                           library configuration
     * @param isEnableUncaughtExceptionHandler is uncaught exception handler should be enabled
     * @param appName                          application name
     * @param appVersion                       application version
     * @return configured Backtrace Client instance
     */
    static BacktraceClient createBacktraceClient(BacktraceConfig config, boolean isEnableUncaughtExceptionHandler, String appName, String appVersion) {

        BacktraceClient client = new BacktraceClient(config);
        if (isEnableUncaughtExceptionHandler) {
            client.enableUncaughtExceptionsHandler();
        }

        if (isStringNotEmpty(appName)) {
            client.setApplicationName(appName);
        }

        if (isStringNotEmpty(appVersion)) {
            client.setApplicationVersion(appVersion);
        }

        return client;
    }

    /**
     * Get attributes from logging event
     *
     * @param logEvent representation of logging events
     * @return map with attributes
     */
    private static Map getAttributes(LogEvent logEvent) {
        Map attributes = new HashMap<>();

        attributes.put(ATTRIBUTE_LOGGING_LEVEL_NAME, logEvent.getLevel().toString());
        Map properties = logEvent.getContextData().toMap();

        if (properties.size() != 0) {
            for (Map.Entry mdcEntry : properties.entrySet()) {
                attributes.put(mdcEntry.getKey(), mdcEntry.getValue());
            }
        }

        return attributes;
    }

    /**
     * Generate BacktraceReport based on logging event
     *
     * @param logEvent representation of logging events
     * @return Backtrace report
     */
    static BacktraceReport createBacktraceReport(LogEvent logEvent) {
        BacktraceReport report;
        Throwable throwable = logEvent.getThrown();
        Map attributes = getAttributes(logEvent);

        if (throwable != null) {
            report = new BacktraceReport((Exception) throwable, attributes);
        } else {
            report = new BacktraceReport(logEvent.getMessage().getFormattedMessage(), attributes);
        }
        return report;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy