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

edu.hm.hafner.util.FilteredLog Maven / Gradle / Ivy

package edu.hm.hafner.util;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;

import com.google.errorprone.annotations.FormatMethod;

/**
 * Provides a log of info messages and a limited number of error messages. If the number of errors exceeds this limit,
 * then subsequent error messages will be skipped. This class is thread-safe and can be used in a distributed
 * environment.
 *
 * @author Ullrich Hafner
 */
public class FilteredLog implements Serializable {
    private static final long serialVersionUID = -8552323621953159904L;

    private static final int DEFAULT_MAX_LINES = 20;

    private final String title;
    private final int maxLines;
    private int lines = 0;

    private final List infoMessages = new ArrayList<>();
    private final List errorMessages = new ArrayList<>();

    private transient ReentrantLock lock = new ReentrantLock();

    /**
     * Creates a new {@link FilteredLog}. The error messages will not have a pre-defined title, you need to make sure
     * that there is a meaningful title before each error message. The maximum number of printed errors is given
     * by {@link #DEFAULT_MAX_LINES}.
     */
    public FilteredLog() {
        this(StringUtils.EMPTY, DEFAULT_MAX_LINES);
    }

    /**
     * Creates a new {@link FilteredLog}. The maximum number of printed errors is given by {@link #DEFAULT_MAX_LINES}.
     *
     * @param title
     *         the title of the error messages
     */
    public FilteredLog(final String title) {
        this(title, DEFAULT_MAX_LINES);
    }

    /**
     * Creates a new {@link FilteredLog}.
     *
     * @param title
     *         the title of the error messages
     * @param maxLines
     *         the maximum number of lines to log
     */
    public FilteredLog(final String title, final int maxLines) {
        this.title = title;
        this.maxLines = maxLines;
    }

    /**
     * Called after de-serialization to improve the memory usage.
     *
     * @return this
     */
    protected Object readResolve() {
        lock = new ReentrantLock();

        return this;
    }

    /**
     * Logs the specified information message. Use this method to log any useful information when composing this log.
     *
     * @param message
     *         the message to log
     */
    public void logInfo(final String message) {
        lock.lock();
        try {
            infoMessages.add(message);
        }
        finally {
            lock.unlock();
        }
    }

    /**
     * Logs the specified information message. Use this method to log any useful information when composing this log.
     *
     * @param format
     *        a format string
     * @param args
     *         Arguments referenced by the format specifiers in the format string. If there are more arguments than
     *         format specifiers, the extra arguments are ignored. The number of arguments is variable and may be
     *         zero.
     */
    @FormatMethod
    public void logInfo(final String format, final Object... args) {
        logInfo(String.format(format, args));
    }

    /**
     * Logs the specified error message. Use this method to log any error when composing this log.
     *
     * @param message
     *         the error message
     */
    public void logError(final String message) {
        lock.lock();
        try {
            if (lines < maxLines) {
                if (StringUtils.isNotBlank(title) && errorMessages.isEmpty()) {
                    errorMessages.add(title);
                }
                errorMessages.add(message);
            }
            lines++;
        }
        finally {
            lock.unlock();
        }
    }

    /**
     * Logs the specified error message. Use this method to log any error when composing this log.
     *
     * @param format
     *         a format string
     * @param args
     *         Arguments referenced by the format specifiers in the format string. If there are more arguments than
     *         format specifiers, the extra arguments are ignored. The number of arguments is variable and may be
     *         zero.
     */
    @FormatMethod
    public void logError(final String format, final Object... args) {
        logError(String.format(format, args));
    }

    /**
     * Logs the specified exception. Use this method to log any exception when composing this log.
     *
     * @param exception
     *         the exception to log
     * @param format
     *         A format string
     * @param args
     *         Arguments referenced by the format specifiers in the format string. If there are more arguments than
     *         format specifiers, the extra arguments are ignored. The number of arguments is variable and may be
     *         zero.
     */
    @FormatMethod
    public void logException(final Exception exception, final String format, final Object... args) {
        logError(format, args);

        lock.lock();
        try {
            if (lines <= maxLines) {
                errorMessages.addAll(Arrays.asList(ExceptionUtils.getRootCauseStackTrace(exception)));
            }
        }
        finally {
            lock.unlock();
        }
    }

    /**
     * Returns the total number of errors that have been reported.
     *
     * @return the total number of errors
     */
    public int size() {
        return lines;
    }

    /**
     * Writes a summary message to the reports' error log that denotes the total number of errors that have been
     * reported.
     *
     * @deprecated not useful anymore
     */
    @Deprecated
    public void logSummary() {
        // do nothing
    }

    /**
     * Returns all info messages.
     *
     * @return the info messages
     */
    public List getInfoMessages() {
        lock.lock();
        try {
            return List.copyOf(infoMessages);
        }
        finally {
            lock.unlock();
        }
    }

    /**
     * Returns all error messages up to the configured limit.
     *
     * @return the error messages
     */
    public List getErrorMessages() {
        lock.lock();
        try {
            var messages = new ArrayList();
            if (errorMessages.isEmpty()) {
                return messages;
            }
            messages.addAll(errorMessages);
            if (lines > maxLines) {
                messages.add(String.format("  ... skipped logging of %d additional errors ...", lines - maxLines));
            }
            return messages;
        }
        finally {
            lock.unlock();
        }
    }

    /**
     * Returns whether there have been error messages recorded.
     *
     * @return {@code true} if error messages have been recorded, {@code false} otherwise
     */
    public boolean hasErrors() {
        lock.lock();
        try {
            return !errorMessages.isEmpty();
        }
        finally {
            lock.unlock();
        }
    }

    /**
     * Merges the info and error messages of the other log.
     *
     * @param other
     *         the log to merge
     */
    public void merge(final FilteredLog other) {
        lock.lock();
        try {
            infoMessages.addAll(other.getInfoMessages());
            errorMessages.addAll(other.getErrorMessages());
        }
        finally {
            lock.unlock();
        }
    }

    @Override
    public boolean equals(final Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        FilteredLog log = (FilteredLog) o;

        if (maxLines != log.maxLines) {
            return false;
        }
        if (lines != log.lines) {
            return false;
        }
        if (!Objects.equals(title, log.title)) {
            return false;
        }
        if (!infoMessages.equals(log.infoMessages)) {
            return false;
        }
        return errorMessages.equals(log.errorMessages);
    }

    @Override
    public int hashCode() {
        int result = title != null ? title.hashCode() : 0;
        result = 31 * result + maxLines;
        result = 31 * result + lines;
        result = 31 * result + infoMessages.hashCode();
        result = 31 * result + errorMessages.hashCode();
        return result;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy